How do I use an explicitly implemented interface p

2019-07-31 17:59发布

问题:

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:

  1. Not every DataContext will contain the property (in case it doesn't, it should be hidden) ... What should I do in this case?
  2. For the DataContexts that do contain the property, it is explicitly implemented ... do you have to cast first or something?

回答1:

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:

  1. 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.
  2. 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.



回答2:

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;       
  }