Here is pseudo code for what I want to implement using a multi-converter.
IF vm.AvatarFilePath IS NOT NULL THEN
Image.Source = {Binding AvatarPath}
ELSE
If vm.Gender == {x:Static vm:Gender.Female} THEN
Image.Source = {StaticResource Img_Female}
ELSE
Image.Source = {StaticResource Img_Male}
ENDIF
ENDIF
Please note that I am not convinced that a multi-converter is the right approach, and I posted a similar question that tries to solve this problem with DataTriggers here.
My attempt at a MultiConverter implementation (below) has issues:
- it crashes with a {Dependency.UnsetValue} which suggests the binding is wrong
- it may or may not be returning the right type, BitmapSource. It needs to handle two sources for the image, either one determined by Gender, a Resx based source, or a file path. I do not know how to catch a bad file path just examing the result, as no error is thrown (I could do a File.Exists but seems overkill).
- The fact that the code is exposed for unit testing is not as useful as it might seem, since I am not aware of an easy and cheap way to compare image equality (see unit test below).
How can I start to clean up this converter code and binding?
Cheers,
Berryl
Converter.Convert code
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
values.ThrowIfNull("values");
foreach (var value in values) {
if (value != null && value.ToString().Equals("{DependencyProperty.UnsetValue}")) return Binding.DoNothing;
}
values[1].ThrowIfNull("gender (value[1])");
var avatarPath = values[0] as string;
BitmapSource result;
if (string.IsNullOrWhiteSpace(avatarPath)) {
var gender = (Gender) values[1];
object bitmapSource;
switch (gender)
{
case Gender.Female:
bitmapSource = DomainSubjects.Img_Female;
break;
default:
bitmapSource = DomainSubjects.Img_Male;
break;
}
result = BitmapHelper.GetBitmapSource(bitmapSource);
}
else {
var bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri(avatarPath, UriKind.RelativeOrAbsolute);
bi.EndInit();
result = bi;
}
return result;
}
BitmapHelper code
public static BitmapSource GetBitmapSource(object source)
{
BitmapSource bitmapSource = null;
if (source is Icon)
{
var icon = source as Icon;
// For icons we must create a new BitmapFrame from the icon data stream
// The approach we use for bitmaps (below) doesn't work when setting the
// Icon property of a window (although it will work for other Icons)
//
using (var iconStream = new MemoryStream())
{
icon.Save(iconStream);
iconStream.Seek(0, SeekOrigin.Begin);
bitmapSource = BitmapFrame.Create(iconStream);
}
}
else if (source is Bitmap)
{
var bitmap = source as Bitmap;
var bitmapHandle = bitmap.GetHbitmap();
bitmapSource = Imaging
.CreateBitmapSourceFromHBitmap(bitmapHandle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
bitmapSource.Freeze();
DeleteObject(bitmapHandle);
}
return bitmapSource;
}
xaml binding
<Image Grid.Column="1" Grid.Row="4"
HorizontalAlignment="Center" Margin="10, 0" Width="96" Height="96" Stretch="UniformToFill"
>
<Image.Source>
<MultiBinding Converter="{StaticResource avatarPathConv}">
<Binding Path="AvatarPath"/>
<Binding Path="Gender"/>
</MultiBinding>
</Image.Source>
</Image>
Unit Test that is useless (extra credit!)
[Test]
public void Convert_AvatarPath_IfBadString_DefaultImage() {
var conv = new AvatarPathConverter();
var result = conv.Convert(new object[] {"blah", Gender.Male}, null, null, CultureInfo.InvariantCulture);
Assert.That(result, Is.EqualTo(DomainSubjects.Img_Male));
}