yamamoWorks

.NET技術を中心に気まぐれに更新していきます

Imageコントロールで画像の取得に失敗するとソースが変わっても画像が表示されなくなる件

WPFのImageコントロールのSourceプロパティをバインドして使用する場面は多々あると思いますが、その際に困った問題があります。
SourceプロパティにバインドされたURLが正しくなかったりアクセス権がなかったりした場合に画像が表示されないのは当然ですが、一度この状態になってしまうと以降に正しいURLを持ったデータがDataContextにセットされても画像が表示されません。

手順① 正しいURLをバインド → 画像が表される
手順② 不正なURLをバインド → 画像が表示されない(ImageFailed)
手順③ 正しいURLをバインド → 画像が表示されない

これを回避する簡単な方法としては、DataContextが変わるたびにSourceプロパティのバインドを再設定する事です。

private void Image_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
((Image)sender).SetBinding(Image.SourceProperty, new Binding("Url"));
}


しかし、この方法ではコードビハインド側にバインドするパスを書くことになり美しくありません。
ならば、Imageコントロールを継承したコントロールを作ってゴニョゴニョ・・・とやりたくなるところですがWindowsフォームアプリケーションではないので、今回はWPFっぽく依存プロパティの仕組みを使った方法で解決してみたいと思います。
といっても、WPFを熟知していないのでちゃんと解説できないのですが(^^;

まず先に、出来上がった時のXAMLはこうなります。


ImageクラスのSourceプロパティではなく、新たに用意するImageBindingHelperクラスのSourceプロパティにバインドの設定をします。

次に、ImageBindingHelperクラスですが、自身のSourceプロパティの値が変わったらImageクラスのSourceプロパティに値をセットするという動きをさせます。

public static class ImageBindingHelper
{
public static readonly DependencyProperty SourceProperty;

static ImageBindingHelper()
{
SourceProperty = DependencyProperty.RegisterAttached(
"Source",
typeof(ImageSource),
typeof(ImageBindingHelper),
new PropertyMetadata (null, OnSourceChanged));
}

public static ImageSource GetSource(DependencyObject d)
{
return d.GetValue(SourceProperty) as ImageSource ;
}

public static void SetSource(DependencyObject d, ImageSource value)
{
d.SetValue(SourceProperty, value);
}

private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Image img = d as Image ;

if (img == null )
{
return ;
}

img.Source = ImageBindingHelper .GetSource(d);
}
}

これで画像が表示されなくなる現象を回避できます。