英文:
Custom WPF image binding in XAML
问题
我继承了一个动态生成XAML并在代码中动态绑定图像对象到位图的WPF项目。下面的示例代码展示了这是如何工作的:
public class XamlImageStore
{
/// <summary>
/// 初始化一个新的XamlImageStore对象。
/// </summary>
static XamlImageStore()
{
images = new Dictionary<string, BitmapSource>();
}
/// <summary>
/// 在XAML中使用的图像。
/// </summary>
private static Dictionary<string, BitmapSource> images;
internal static void AddImage(string imageID, BitmapSource image)
{
images[imageID] = image;
}
public static string GetImageId(DependencyObject obj)
{
return (string)obj.GetValue(ImageIdProperty);
}
public static void SetImageId(DependencyObject obj, string value)
{
obj.SetValue(ImageIdProperty, value);
}
public static readonly DependencyProperty ImageIdProperty =
DependencyProperty.RegisterAttached("ImageId", typeof(string), typeof(XamlImageStore), new UIPropertyMetadata("0", ImageIdChanged));
private static void ImageIdChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
Image target = obj as Image;
if (target != null)
{
string imageID = e.NewValue.ToString();
if (images.ContainsKey(imageID))
{
// 设置图像源。
target.Source = images[imageID];
}
}
}
}
主应用程序窗口(应用程序命名空间和输出的exe为DynamicXamlImage):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string customImageID = "ABCD";
XamlImageStore.AddImage(customImageID, GetBitmap());
MemoryStream ms = new MemoryStream();
XmlTextWriter xamlWriter = new XmlTextWriter(ms, Encoding.UTF8);
xamlWriter.WriteStartElement("Canvas");
xamlWriter.WriteAttributeString("xmlns", "http://schemas.microsoft.com/xps/2005/06");
xamlWriter.WriteAttributeString("xmlns:local", "clr-namespace:DynamicXamlImage;assembly=DynamicXamlImage");
xamlWriter.WriteStartElement("Image");
xamlWriter.WriteAttributeString("local:XamlImageStore.ImageId", customImageID);
xamlWriter.WriteEndElement();
xamlWriter.WriteEndElement();
xamlWriter.Flush();
ms.Position = 0;
Canvas canvas = (Canvas)XamlReader.Load(ms);
appGrid.Children.Add(canvas);
}
private WriteableBitmap GetBitmap()
{
// 这里是获取位图的代码。
// ...
}
这是输出结果。
你问如何解释这个绑定是如何工作的,因为你需要扩展XAML生成代码以支持动态生成的不透明蒙版,但不知道如何使ImageBrush以相同的方式加载蒙版。如果有关这个绑定的详细信息,请告诉我。
英文:
I have inherited a WPF project that generates XAML dynamically and binds Image objects to bitmaps also generated dynamically in code. Sample code below shows how this works:
public class XamlImageStore
{
/// <summary>
/// Initializes a new <see cref="XamlImageStore"/> object.
/// </summary>
static XamlImageStore()
{
images = new Dictionary<string, BitmapSource>();
}
/// <summary>
/// Images used in XAML.
/// </summary>
private static Dictionary<string, BitmapSource> images;
internal static void AddImage(string imageID, BitmapSource image)
{
images[imageID] = image;
}
public static string GetImageId(DependencyObject obj)
{
return (string)obj.GetValue(ImageIdProperty);
}
public static void SetImageId(DependencyObject obj, string value)
{
obj.SetValue(ImageIdProperty, value);
}
public static readonly DependencyProperty ImageIdProperty =
DependencyProperty.RegisterAttached("ImageId", typeof(string), typeof(XamlImageStore), new UIPropertyMetadata("0", ImageIdChanged));
private static void ImageIdChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
Image target = obj as Image;
if (target != null)
{
string imageID = e.NewValue.ToString();
if (images.ContainsKey(imageID))
{
// Set the image source.
target.Source = images[imageID];
}
}
}
}
Main application window (application namespace and output exe is DynamicXamlImage):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string customImageID = "ABCD";
XamlImageStore.AddImage(customImageID, GetBitmap());
MemoryStream ms = new MemoryStream();
XmlTextWriter xamlWriter = new XmlTextWriter(ms, Encoding.UTF8);
xamlWriter.WriteStartElement("Canvas");
xamlWriter.WriteAttributeString("xmlns", "http://schemas.microsoft.com/xps/2005/06");
xamlWriter.WriteAttributeString("xmlns:local", "clr-namespace:DynamicXamlImage;assembly=DynamicXamlImage");
xamlWriter.WriteStartElement("Image");
xamlWriter.WriteAttributeString("local:XamlImageStore.ImageId", customImageID);
xamlWriter.WriteEndElement();
xamlWriter.WriteEndElement();
xamlWriter.Flush();
ms.Position = 0;
Canvas canvas = (Canvas)XamlReader.Load(ms);
appGrid.Children.Add(canvas);
}
private WriteableBitmap GetBitmap()
{
WriteableBitmap wb = new WriteableBitmap(20, 20, 96, 96, PixelFormats.Gray8, null);
byte[] pixels = new byte[] {
0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0,
};
wb.WritePixels(new Int32Rect(0, 0, 20, 20), pixels, 20, 0);
return wb;
}
Can somebody explain how this binding works as I could not find anything on this method?
The reason I ask is that I have to extend the XAML generation code to support opacity masks that are generated dynamically and I have no idea how to make an ImageBrush load the mask in the same way.
答案1
得分: 1
以下是翻译好的内容:
能有人解释一下这个绑定是如何工作的吗?
这里没有涉及数据绑定。
XamlImageStore 由两部分组成:
- 一个将ID字符串映射到
BitmapSource实例的字典 - 一个名为
ImageId的附加属性,带有一个属性更改回调,将字典中的BitmapSource分配给已设置附加属性的图像元素的Source属性。
在代码示例中,从 GetBitmap() 方法返回的 BitmapSource 被添加到具有键 "ABCD" 的字典中。然后,将附加属性设置在具有值 "ABCD" 的图像元素上,这将导致图像的 Source 被设置为先前创建的 BitmapSource。
作为一种注意,XamlImageStore 类的静态构造函数是多余的。您可以写成:
private static readonly Dictionary<string, BitmapSource> images
= new Dictionary<string, BitmapSource>();
PropertyChangedCallback 也可以写得更加高效:
private static void ImageIdChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (obj is Image target)
{
string imageID = e.NewValue.ToString();
if (images.TryGetValue(imageID, out BitmapSource bitmap))
{
target.Source = bitmap;
}
}
}
还要注意,拥有这个辅助类的整个想法可能是多余的。您也可以使用XAML资源,例如:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string customImageID = "ABCD";
// *** 看这里 ***
Resources[customImageID] = GetBitmap();
MemoryStream ms = new MemoryStream();
XmlTextWriter xamlWriter = new XmlTextWriter(ms, Encoding.UTF8);
xamlWriter.WriteStartElement("Canvas");
xamlWriter.WriteAttributeString("xmlns", "http://schemas.microsoft.com/xps/2005/06");
xamlWriter.WriteAttributeString("xmlns:local", "clr-namespace:DynamicXamlImage;assembly=DynamicXamlImage");
xamlWriter.WriteStartElement("Image");
// *** 和这里 ***
xamlWriter.WriteAttributeString("Source", $"{{DynamicResource {customImageID}}}");
xamlWriter.WriteEndElement();
xamlWriter.WriteEndElement();
xamlWriter.Flush();
ms.Position = 0;
Canvas canvas = (Canvas)XamlReader.Load(ms);
appGrid.Children.Add(canvas);
}
英文:
> Can somebody explain how this binding works?
There is no data binding involved.
XamlImageStore consists of two parts:
- a Dictionary that maps ID strings to
BitmapSourceinstances - an attached property named
ImageIdwith a PropertyChanged callback that assigns a BitmapSource from the Dictionary to theSourceproperty of an Image element on which the attached property was set.
In the code sample a BitmapSource that is returned from the GetBitmap() method is added to the Dictionary with the key "ABCD". Then the attached property is set on an Image element with value "ABCD", which results in the Source of the Image set to the previously created BitmapSource.
As a note, the static constructor of the XamlImageStore class is redundant. You could as well write
private static readonly Dictionary<string, BitmapSource> images
= new Dictionary<string, BitmapSource>();
The PropertyChangedCallback could also be written a bit more efficient:
private static void ImageIdChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (obj is Image target)
{
string imageID = e.NewValue.ToString();
if (images.TryGetValue(imageID, out BitmapSource bitmap))
{
target.Source = bitmap;
}
}
}
Also be aware that the whole idea of having this helper class may be redundant. You could as well use XAML Resources, e.g. like this:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string customImageID = "ABCD";
// *** see here ***
Resources[customImageID] = GetBitmap();
MemoryStream ms = new MemoryStream();
XmlTextWriter xamlWriter = new XmlTextWriter(ms, Encoding.UTF8);
xamlWriter.WriteStartElement("Canvas");
xamlWriter.WriteAttributeString("xmlns", "http://schemas.microsoft.com/xps/2005/06");
xamlWriter.WriteAttributeString("xmlns:local", "clr-namespace:DynamicXamlImage;assembly=DynamicXamlImage");
xamlWriter.WriteStartElement("Image");
// *** and here ***
xamlWriter.WriteAttributeString("Source", $"{{DynamicResource {customImageID}}}");
xamlWriter.WriteEndElement();
xamlWriter.WriteEndElement();
xamlWriter.Flush();
ms.Position = 0;
Canvas canvas = (Canvas)XamlReader.Load(ms);
appGrid.Children.Add(canvas);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。



评论