web-dev-qa-db-ja.com

Image.SourceをWPFの文字列にバインドしますか?

以下のXAMLコードがあります:

<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文字列から画像を作成するメソッドがあります。

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;
    }
} 

次に、TestImageBase64StringToImageメソッドの出力にバインドします。
私は次の方法を使用しました:

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文字列が正しいことを確信しています

8
Mohammad Dayyan

@itowlsonの優れた回答を補完するものとして、コードは次のようになります。

// 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));
        }
    }
}
16
Simon Buchan

あなたがしていることを分解しましょう。

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

これが機能するためには、バインディングソースがImageSourceか、画像ファイルへのURIを表す文字列である必要があります。それでは、ImageSourceプロパティが実際に何であるかを見てみましょう。

_public string ImageSource { get; set; }
_

ここでの1つの問題は、ImageSourceがPropertyChangedイベントを発生させていないことです。そのため、プロパティを更新しても、WPFはバインディングターゲットを更新しません。

ただし、ImageSourceはImageSourceではなく、文字列です。それは問題ありませんが、WPFはその文字列をURIとして解釈します。そのURIは何ですか?

_ImageSource = Base64StringToImage(BIG_HONKING_STRING).Source.ToString(); 
_

これはあなたの問題の核心です。画像はアドレス可能なリソースではないため、ImageSource文字列は実際にはURIではありません。 Base64StringToImageは、base64文字列からメモリ内のImageSourceを作成し、それをソースとして持つImageを返します。次に、ImageのSource(ImageSourceオブジェクト)を取得し、それを文字列化します。 ImageSourceがファイルまたはURLからのものである場合は機能する可能性がありますが、機能しませんでした。HBITMAPからのものです。したがって、ToString()の結果は無意味になります。そのため、ImageSourceは意味のないものに設定されており、Imageはこの意味のないものをビットマップファイルのURLとして解釈しようとしています。

したがって、これを修正するには、次の3つのことを行う必要があります。

  1. ImageSourceプロパティのPropertyChangedイベントを発生させます(または依存関係プロパティにします)。
  2. ImageSourceプロパティを文字列型ではなくImageSource型に変更します(URLのない画像ソースを含めることができるようにします)。
  3. セッター呼び出しを変更して、ImageSourceをBase64StringToImage(...).Sourceに設定します。つまり、ToString()呼び出しを削除します。さらに良いことに、Base64StringToImageを変更して、ImageではなくImageSourceを返します。Image要素を作成すると、本当に関心があるのはBitmapSourceだけなので、オーバーヘッドが発生するだけです。
22
itowlson