I have the following situation:
I have a few ViewModel objects, some of which implement an interface ISomeInterface
, some don't. The interfaces exposes a property called SomeEnumeration
(IEnumerable<T>
).
For example:
public sealed class ViewModelA : ViewModelBase, ISomeInterface
{
// ...
IEnumerable<Foo> ISomeInterface.SomeEnumeration
{
get { ...; }
}
}
public sealed class ViewModelB : ViewModelBase
{
// ...
}
My XAML, so far, has been designed in a way that both of the ViewModels happen to have the properties I am binding against (i.e. PropertyA
, PropertyB
, etc.). I haven't ran into the situation yet where a property I am binding against does not exist on the ViewModels that I am setting as the DataContext
. But, now I will... and it will be against a property that is explicitly implemented (I'm not sure if that makes any difference in the WPF Binding Engine).
Basically, my xaml will look like the following:
<StackPanel
Visiblity="{Binding Path=SomeEnumeration, Converter={StaticResource AnyConverter}">
...
</StackPanel>
I'm not sure if this will even work because:
- Not every
DataContext
will contain the property (in case it doesn't, it should be hidden) ... What should I do in this case?
- For the
DataContext
s that do contain the property, it is explicitly implemented ... do you have to cast first or something?
Generally, when you want to use the WPF DataBinding Engine, you'll want to also utilize the FallbackValue
and the TargetNullValue
binding properties. What do these exactly do?
FallbackValue: Gets or sets the value when the binding is unable to
return a value.
TargetNullValue: Gets or sets the value that is used
in the target when the value of the source is null.
Jon explains the binding engine pretty well in this answer:
Binding.DoNothing is an object instance that you actively return from
a value converter; it instructs the binding engine to not update the
value of the target property at all. Here's a nice example by Josh
Smith of what you might use this for.
FallbackValue is a property that you set on bindings; it allows you to
specify the value to be applied to the target property if:
- the binding source cannot be resolved (e.g. wrong binding path), or
- the binding property value is equal to DependencyProperty.UnsetValue, or
- a value converter used for the binding throws an exception, or
- a value converter used for the binding returns DependencyProperty.UnsetValue, or
- the value produced by the binding pipeline is not valid for the target property (e.g. wrong type)
TargetNullValue is also a property you set on bindings; it allows you
to specify the value to be applied to the target property if the value
of the source property is null. For example, if you bind a text box to
a string property TargetNullValue lets you pick what appears in the
text box if the source string is null.
As far as binding to "explicitly implemented interface", the real question should be how do you set the path to an interface property, because how that interface is implemented does not matter. This is actually quite easy to do in XAML, and here is an example:
<TextBox Text="{Binding Path=(local:ISomeInterface.SomeProperty)}" />
So, to answer your questions directly:
- Utilize
FallbackValue
(and optionally TargetNullValue
if necessary). For example, pass in null when the binding value can not be resolved due to a binding error.
- Utilize the correct pattern for binding the Path property to an interface's property (see above example).
XAML usage:
<StackPanel Visiblity="{Binding Path=(local:ISomeInterface.SomeEnumeration),
Converter={StaticResource AnyConverter},
FallbackValue={x:Null}}">
...
</StackPanel>
One final note: If the binding fails early, the null FallbackValue
would not be the value passed into the converter, it would be the final value used whether the binding fails at the property level or the converter level or etc. So do not expect that the converter will still run while passing in null into it.
A quick and good fix for you situation would be to place all your logic in the converter which is already in place .
xaml : (your binding)
<StackPanel
Visiblity="{Binding Path=., Converter={StaticResource AnyConverter}">
...
</StackPanel>
cs : (your Converter)
Convert()
{
return value Is ISomeInterface ?
(((ISomeInterface)value).SomeEnumeration == SomeEnumeration.SomeValue ?
Visibility.Visible : Visibility.Collapsed) : Visibility.Collapsed;
}