自定义WPF图像绑定在XAML中

huangapple go评论73阅读模式
英文:

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
{
    /// &lt;summary&gt;
    /// Initializes a new &lt;see cref=&quot;XamlImageStore&quot;/&gt; object.
    /// &lt;/summary&gt;
    static XamlImageStore()
    {
        images = new Dictionary&lt;string, BitmapSource&gt;();
    }

    /// &lt;summary&gt;
    /// Images used in XAML.
    /// &lt;/summary&gt;
    private static Dictionary&lt;string, BitmapSource&gt; 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(&quot;ImageId&quot;, typeof(string), typeof(XamlImageStore), new UIPropertyMetadata(&quot;0&quot;, 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 = &quot;ABCD&quot;;
    XamlImageStore.AddImage(customImageID, GetBitmap());

    MemoryStream ms = new MemoryStream();
    XmlTextWriter xamlWriter =  new XmlTextWriter(ms, Encoding.UTF8);

    xamlWriter.WriteStartElement(&quot;Canvas&quot;);
    xamlWriter.WriteAttributeString(&quot;xmlns&quot;, &quot;http://schemas.microsoft.com/xps/2005/06&quot;);
    xamlWriter.WriteAttributeString(&quot;xmlns:local&quot;, &quot;clr-namespace:DynamicXamlImage;assembly=DynamicXamlImage&quot;);

    xamlWriter.WriteStartElement(&quot;Image&quot;);
    xamlWriter.WriteAttributeString(&quot;local:XamlImageStore.ImageId&quot;, 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;
}

This is the output:
自定义WPF图像绑定在XAML中

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 被添加到具有键 &quot;ABCD&quot; 的字典中。然后,将附加属性设置在具有值 &quot;ABCD&quot; 的图像元素上,这将导致图像的 Source 被设置为先前创建的 BitmapSource


作为一种注意,XamlImageStore 类的静态构造函数是多余的。您可以写成:

private static readonly Dictionary&lt;string, BitmapSource&gt; images
    = new Dictionary&lt;string, BitmapSource&gt;();

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 = &quot;ABCD&quot;;

    // *** 看这里 ***
    Resources[customImageID] = GetBitmap();

    MemoryStream ms = new MemoryStream();
    XmlTextWriter xamlWriter = new XmlTextWriter(ms, Encoding.UTF8);

    xamlWriter.WriteStartElement(&quot;Canvas&quot;);
    xamlWriter.WriteAttributeString(&quot;xmlns&quot;, &quot;http://schemas.microsoft.com/xps/2005/06&quot;);
    xamlWriter.WriteAttributeString(&quot;xmlns:local&quot;, &quot;clr-namespace:DynamicXamlImage;assembly=DynamicXamlImage&quot;);

    xamlWriter.WriteStartElement(&quot;Image&quot;);

    // *** 和这里 ***
    xamlWriter.WriteAttributeString(&quot;Source&quot;, $&quot;{{DynamicResource {customImageID}}}&quot;);
    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 the Source 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 &quot;ABCD&quot;. Then the attached property is set on an Image element with value &quot;ABCD&quot;, 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&lt;string, BitmapSource&gt; images
    = new Dictionary&lt;string, BitmapSource&gt;();

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 = &quot;ABCD&quot;;

    // *** see here ***
    Resources[customImageID] = GetBitmap();

    MemoryStream ms = new MemoryStream();
    XmlTextWriter xamlWriter = new XmlTextWriter(ms, Encoding.UTF8);

    xamlWriter.WriteStartElement(&quot;Canvas&quot;);
    xamlWriter.WriteAttributeString(&quot;xmlns&quot;, &quot;http://schemas.microsoft.com/xps/2005/06&quot;);
    xamlWriter.WriteAttributeString(&quot;xmlns:local&quot;, &quot;clr-namespace:DynamicXamlImage;assembly=DynamicXamlImage&quot;);

    xamlWriter.WriteStartElement(&quot;Image&quot;);

    // *** and here ***
    xamlWriter.WriteAttributeString(&quot;Source&quot;, $&quot;{{DynamicResource {customImageID}}}&quot;);
    xamlWriter.WriteEndElement();

    xamlWriter.WriteEndElement();

    xamlWriter.Flush();
    ms.Position = 0;

    Canvas canvas = (Canvas)XamlReader.Load(ms);
    appGrid.Children.Add(canvas);
}

huangapple
  • 本文由 发表于 2023年7月10日 17:04:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76652248.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定