【WPF】WPFのUserControlで、Designer上でダミー画像が表示されるようにする
TL;DR;
<UserControl ... >
...
<UserControl.DataContext>
<local:MockData />
</UserControl.DataContext>
...
</UserControl>
<UserControl ... >
...
<UserControl.DataContext>
<local:MockData />
</UserControl.DataContext>
...
</UserControl>
これで利用側は表示され続けます。
しかし、UserControl側はリビルドすると画像だけ消えます。
やりたかったこと
WPFやUWPの利点の一つにXAMLでUIが描けること、そしてVisualStudioではXAMLのデザイナー(グラフィカルエディタ)が搭載されていることがあります。
折角そんな素晴らしい機能があるので、UIの見た目をリアルタイムで確認しながらXAMLを書きたいものです。
しかし、StackPanel
などの自動レイアウトを使っていると、画像が読み込まれているときとそうでないときでImage
コントロールのサイズが変わってしまい、デザイナー上での見た目が潰れて大変悲しいことになってしまいます。
これを何とかするために、実行時にBindする予定の部分にも何かしらダミーの画像を当てておいて、ある程度のサイズを確保してくれるようにしたかったのです。
駄目だったパターン
FallbackValue
、TargetNullValue
について
FallbackValue
、TargetNullValue
について「xaml Default Designer」みたいなノリで検索すると、Binding
にBindingBase.FallbackValue
やBindingBase.TargetNullValue
を設定すればいいよ!といった感じの情報が見つかりました。
しかし、TextBlock.Text
などの場合はうまくいくのですが、Image.Source
については描いた瞬間は表示されるものの暫く経つor利用側のデザイナーで表示がされなくなってしまいます。
はじめは、Resourcesから供給される画像がBitmap
なのとImage.Source
がImageSource
の差の問題かとも思ったのですが、適切なConverterをかませても、暫く経つと表示されなくなってしまっていました。
<Image Source="{
Binding img,
FallbackValue={StaticResource prop.Resources.fallback_image_icon},
Converter={StaticResource local:DefaultImageWhenNull}}"/>
<!-- DefaultImageWhenNullは [ValueConversion(typeof(Bitmap), typeof(ImageSource))] なValueConverter -->
Converterで入力がnullの時にデフォルトの画像を返す方法
OSSの実装例をあさっていたところ、以下のようにConverterでデフォルト画像を指定しているところがありました。
ということでこれも試してみたのですが、結果はDataContext
がBindされるまでConverterも動かない(FallbackValue
がついていても動かない)ということで駄目でした。
実際、元PJを開いてみてもそもそもデザイナ自体表示できなくなっていたので、私のやりたかったことの答えではない意図っぽいです。
たぶん実行時のnullヨケですね。
[ValueConversion(typeof(ImageSource), typeof(ImageSource))]
public class ImageSourceToThumbnailConverter : IValueConverter
{
static readonly ImageSource _defaultThumbnail = MainWindow.Current.Resources["thumbnail_default"] as ImageSource;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> value ?? _defaultThumbnail;
...
}
d:DataContext
を設定する。
UserControl側
<UserControl
...
d:DataContext="{
d:DesignInstance local:MockData,
IsDesignTimeCreatable=True}">
...
<Image Source="{Binding img}" />
...
</UserControl>
...
public class MockData
{
// 返す値はお好みで
// 今回はプロジェクトプロパティのResource画像から
public ImageSource img { get; }
= Imaging.CreateBitmapSourceFromHBitmap(
Properties.Resources.fallback_image_icon.GetHbitmap(),
IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
利用側
何も考えずに取り込めばOK。
<Window ... >
...
<local:PageItem />
<local:PageItem />
<local:PageItem />
...
</Window>
……と思っていたのですが、リビルドをかけた瞬間画像が吹っ飛びました。
どうも、時間が経つと消えるのではなく、リビルドが走ると消えるようです。
というのもリビルドしても、なぜかDataContextに入っているインスタンスがリロードされていない、つまりリビルド以前のインスタンスが入っています。
本当かよって感じですが、GetHashCode
をTextBlockにダンプしてみたところ、値は変わりませんでした。
ここからは推測ですが、リビルドをしてアセンブリは切り替わりそれまでのメモリも解放されているのだと思います。
しかし、Image側はそれを握り続けているため解放済みの無効な画像を表示しようとしてもできないくなっていたのではないかと思います。
現に、ImageSourceもダンプしてみましたが、リビルドしてもnullにはなっていませんでした。
意味不明ですね。
参考
Default value at design time XAML(Stackoverflow)
Ito Mitsuhiro/NeeView ImageSourceToThumbnailConverter.cs(BitBucket)
XAMLデザイナ専用ViewModelコンストラクタの作り方(Qiita)
Author And Source
この問題について(【WPF】WPFのUserControlで、Designer上でダミー画像が表示されるようにする), 我々は、より多くの情報をここで見つけました https://qiita.com/up-hash/items/73db6f45ce8d91e1b3d0著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .