I have the following property in my ViewModel
public IEquipment Equipment
{
get
{
return equipment;
}
set
{
if (equipment != value)
{
equipment = value;
InvokePropertyChanged("Equipment");
}
}
}
This item itself has a bool
property, which is bound to an Ellipse
in my View, which I want to use as a indicator item:
<Ellipse Width="10" Height="10" Fill="{Binding Equipment.IsAvailable, Converter={StaticResource BoolToColorConverter}, FallbackValue=DarkGray}" Margin="1"/>
The BoolToColorConverter
simply converts the color to either green (true
) or red (false
). During runtime Equipment
can be an instance of one of two class types which inherit from IEquipment
. Only one of them has the IsAvailable
property. In practice this works fine, I get eighter my red or green color...or a gray one, in case the other type of equipment is active.
Problem is, that each time the GUI updates, the following warning gets output:
System.Windows.Data Warning: 40 : BindingExpression path error: 'IsAvailable' property not found on 'object'
How can I avoid this issue? Basically I want to bind to this property only if it is of the correct type. I can think of two solutions, which I'm not particularly fond of:
- Simply add the
IsAvailable
property to the other type and set it to null (BoolToColorConverter
can handle null values and returns dark grey): This might be ok for a simplebool
, but in my actual case there are other items, which are quite class specific. - Do the databinding in the code-behind: This might work. Using an event like
Loaded
on startup to set the binding manually at runtime based on the type. However, this might be troublesome for debugging later, because all other Bindings in the project happen directly in the xaml file. Additionally,Equipment
might change during the lifetime of the ViewModel, so I would have to somehow track it.
CodePudding user response:
Xaml doesn't bind to interfaces, it binds to concrete types.
If your types have different properties, then you need different xaml to bind them.
Use DataTemplates to specify different xaml for displaying each type.
CodePudding user response:
If the properties on your derivatives of IEquipment
(here Equipment
and OtherEquipment
as examples) differ a lot and do not share a common interface, they most likely differ in their appearance. In this case you would need different DataTemplate
s for each type. This is an example for a ContentControl
, but it works the same fot ItemsContol
s with implicit data templates (no x:Key
, but a DataType
) that are applied automatically.
<ContentControl Content="{Binding Equipment}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:Equipment}">
<Ellipse Width="10" Height="10" Fill="{Binding IsAvailable, Converter={StaticResource BoolToColorConverter}, FallbackValue=DarkGray}" Margin="1"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:OtherEquipment}">
<Ellipse Width="10" Height="10" Fill="DarkGray" Margin="1"/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
A workaround for your specific issue could be writing a custom, specialized value converter.
public class EquipmentAvailabilityToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Equipment equipment)
return equipment.IsAvailable ? Brushes.Green : Brushes.Red;
return (Brush)parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
<Ellipse Width="10" Height="10" Fill="{Binding Equipment, Converter={StaticResource EquipmentAvailabilityToColorConverter}, ConverterParameter={x:Static Brushes.DarkGray}}" Margin="1"/>