关于c#:在WPF中将Image.Source绑定到字符串?

Binding Image.Source to String in WPF?

我有以下XAML代码:

1
2
3
4
5
6
7
8
9
10
11
12
<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    WindowStartupLocation="CenterScreen"
    Title="Window1" Height="300" Width="300">

    <Grid>
        <Image x:Name="TestImage" Source="{Binding Path=ImageSource}" />
    </Grid>

</Window>

此外,还有一种方法可以从Base64字符串生成图像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Image Base64StringToImage(string base64ImageString)
{
    try
    {
        byte[] b;
        b = Convert.FromBase64String(base64ImageString);
        MemoryStream ms = new System.IO.MemoryStream(b);
        System.Drawing.Image img = System.Drawing.Image.FromStream(ms);

        //////////////////////////////////////////////
        //convert System.Drawing.Image to WPF image
        System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(img);
        IntPtr hBitmap = bmp.GetHbitmap();
        System.Windows.Media.ImageSource imageSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());

        Image wpfImage = new Image();
        wpfImage.Source = imageSource;
        wpfImage.Width = wpfImage.Height = 16;
        //////////////////////////////////////////////

        return wpfImage;
    }
    catch
    {
        Image img1 = new Image();
        img1.Source = new BitmapImage(new Uri(@"/passwordManager;component/images/TreeView/empty-bookmark.png", UriKind.Relative));
        img1.Width = img1.Height = 16;
        return img1;
    }
}

现在,我将TestImage绑定到Base64StringToImage方法的输出。
我使用以下方式:

1
2
public string ImageSource { get; set; }
ImageSource = Base64StringToImage("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAABjUExURXK45////6fT8PX6/bTZ8onE643F7Pf7/pDH7PP5/dns+b7e9MPh9Xq86NHo947G7Hm76NTp+PL4/bHY8ojD67rc85bK7b3e9MTh9dLo97vd8/D3/Hy96Xe76Nfr+H+/6f///1bvXooAAAAhdFJOU///////////////////////////////////////////AJ/B0CEAAACHSURBVHjaXI/ZFoMgEEMzLCqg1q37Yv//KxvAlh7zMuQeyAS8d8I2z8PT/AMDShWQfCYJHL0FmlcXSQTGi7NNLSMwR2BQaXE1IfAguPFx5UQmeqwEHSfviz7w0BIMyU86khBDZ8DLfWHOGPJahe66MKe/fIupXKst1VXxW/VgT/3utz99BBgA4P0So6hyl+QAAAAASUVORK5CYIII").Source.ToString();

但什么也没发生。
我该如何解决?

顺便说一句,我很确定base64字符串是正确的


让我们分解一下你在做什么。

1
<Image Source="{Binding ImageSource}" />

为此,绑定源必须是ImageSource或表示图像文件URI的字符串。因此,让我们看一下ImageSource属性实际上是什么。

1
public string ImageSource { get; set; }

这里的一个问题是ImageSource没有引发PropertyChanged事件。因此,当您更新属性时,WPF不会更新绑定目标。

但是,ImageSource不是ImageSource,它是一个字符串。没关系,但是WPF会将字符串解释为URI。该URI是什么?

1
ImageSource = Base64StringToImage(BIG_HONKING_STRING).Source.ToString();

这是您的问题所在。 ImageSource字符串实际上不是URI,因为您的图像不是可寻址资源。 Base64StringToImage从base64字符串创建一个内存ImageSource,然后返回一个以ImageImage作为其Source的Image。然后,获取图像的Source(它是一个ImageSource对象),并将其字符串化。如果ImageSource来自文件或URL,则可能会起作用,但并非如此:它来自HBITMAP。因此,ToString()的结果将毫无意义。因此,将ImageSource设置为无意义的东西,并且您的Image试图将无意义的东西解释为位图文件的URL。

因此,要解决此问题,您需要做三件事:

  • 引发ImageSource属性的PropertyChanged事件(或使其成为依赖项属性)。
  • 将ImageSource属性更改为ImageSource类型,而不是字符串类型(以便它可以包含无URL的图像源)。
  • 更改您的setter调用以将ImageSource设置为Base64StringToImage(...).Source-即删除ToString()调用。更妙的是,将Base64StringToImage更改为返回ImageSource而不是Image:创建Image元素只会增加开销,因为您真正感兴趣的只是BitmapSource。

  • 作为@itowlson出色答案的补充,这是您的代码应如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    // MainWindow.xaml
    <Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <DockPanel>
            <Image Source="{Binding ImageSource}" />
        </DockPanel>
    </Window>

    // MainWindow.xaml.cs
    using System.ComponentModel;
    using System.IO;
    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var model = new MainModel();
            DataContext = model;

            model.SetImageData(File.ReadAllBytes(@"C:\\Users\\Public\\Pictures\\Sample Pictures\\Desert.jpg"));
        }
    }

    class MainModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void SetImageData(byte[] data) {
            var source = new BitmapImage();
            source.BeginInit();
            source.StreamSource = new MemoryStream(data);
            source.EndInit();

            // use public setter
            ImageSource = source;
        }

        ImageSource imageSource;
        public ImageSource ImageSource
        {
            get { return imageSource; }
            set
            {
                imageSource = value;
                OnPropertyChanged("ImageSource");
            }
        }

        protected void OnPropertyChanged(string name)
        {
            var handler = PropertyChanged;
            if (null != handler)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }