英文:
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
BitmapSource
instances - an attached property named
ImageId
with a PropertyChanged callback that assigns a BitmapSource from the Dictionary to theSource
property 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);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论