Как правильно использовать явно реализованное свойство интерфейса и видимость wpf?
У меня следующая ситуация:
У меня есть несколько объектов ViewModel, некоторые из которых реализуют интерфейс ISomeInterface
некоторые не делают. Интерфейсы предоставляют свойство под названием SomeEnumeration
(IEnumerable<T>
).
Например:
public sealed class ViewModelA : ViewModelBase, ISomeInterface
{
// ...
IEnumerable<Foo> ISomeInterface.SomeEnumeration
{
get { ...; }
}
}
public sealed class ViewModelB : ViewModelBase
{
// ...
}
Мой XAML до сих пор был спроектирован таким образом, что обе модели ViewModel имеют свойства, с которыми я связываюсь (т.е. PropertyA
, PropertyB
, так далее.). Я еще не сталкивался с ситуацией, когда свойство, с которым я связываюсь, не существует в моделях представления, которые я устанавливаю как DataContext
, Но теперь я буду... и это будет против свойства, которое явно реализовано (я не уверен, имеет ли это какое-то значение в механизме связывания WPF).
По сути, мой xaml будет выглядеть следующим образом:
<StackPanel
Visiblity="{Binding Path=SomeEnumeration, Converter={StaticResource AnyConverter}">
...
</StackPanel>
Я не уверен, что это сработает, потому что:
- Не каждый
DataContext
будет содержать свойство (в противном случае оно должно быть скрыто) ... Что мне делать в этом случае? - Для
DataContext
s, которые содержат свойство, оно явно реализовано... вы должны сначала привести в действие что ли?
2 ответа
Как правило, когда вы хотите использовать механизм привязки данных WPF, вы также должны использовать FallbackValue
и TargetNullValue
обязательные свойства. Что именно они делают?
FallbackValue: получает или задает значение, когда привязка не может вернуть значение.
TargetNullValue: получает или задает значение, которое используется в цели, когда значение источника равно нулю.
Джон очень хорошо объясняет механизм привязки в этом ответе:
Binding.DoNothing - это экземпляр объекта, который вы активно возвращаете из преобразователя значений; он указывает механизму привязки вообще не обновлять значение целевого свойства. Вот хороший пример Джоша Смита, для чего вы можете использовать это.
FallbackValue - это свойство, которое вы устанавливаете для привязок; это позволяет вам указать значение, которое будет применено к целевому свойству, если:
- источник привязки не может быть разрешен (например, неверный путь привязки), или
- значение свойства привязки равно DependencyProperty.UnsetValue или
- преобразователь значения, используемый для привязки, генерирует исключение, или
- конвертер значений, используемый для привязки, возвращает DependencyProperty.UnsetValue или
- значение, созданное конвейером привязки, недопустимо для целевого свойства (например, неправильный тип)
TargetNullValue также является свойством, которое вы устанавливаете для привязок; это позволяет вам указать значение, которое будет применено к целевому свойству, если значение исходного свойства равно нулю. Например, если вы связываете текстовое поле со строковым свойством, TargetNullValue позволяет вам выбрать то, что появляется в текстовом поле, если исходная строка равна нулю.
Что касается привязки к "явно реализованному интерфейсу", реальный вопрос должен заключаться в том, как задать путь к свойству интерфейса, потому что способ реализации этого интерфейса не имеет значения. На самом деле это довольно легко сделать в XAML, и вот пример:
<TextBox Text="{Binding Path=(local:ISomeInterface.SomeProperty)}" />
Итак, чтобы ответить на ваши вопросы напрямую:
- использовать
FallbackValue
(и опциональноTargetNullValue
если необходимо). Например, передайте значение null, если значение привязки не может быть разрешено из-за ошибки привязки. - Используйте правильный шаблон для привязки свойства Path к свойству интерфейса (см. Пример выше).
Использование XAML:
<StackPanel Visiblity="{Binding Path=(local:ISomeInterface.SomeEnumeration),
Converter={StaticResource AnyConverter},
FallbackValue={x:Null}}">
...
</StackPanel>
И последнее замечание: если привязка завершается неудачно рано, значение NULL FallbackValue
не будет значением, передаваемым в преобразователь, это будет конечное значение, используемое независимо от того, будет ли сбой привязки на уровне свойств, на уровне преобразователя и т. д. Поэтому не ожидайте, что преобразователь все равно будет работать, передавая ему значение null.
Быстрое и хорошее решение для вашей ситуации - поместить всю вашу логику в уже установленный конвертер.
xaml: (твоя привязка)
<StackPanel
Visiblity="{Binding Path=., Converter={StaticResource AnyConverter}">
...
</StackPanel>
CS: (ваш конвертер)
Convert()
{
return value Is ISomeInterface ?
(((ISomeInterface)value).SomeEnumeration == SomeEnumeration.SomeValue ?
Visibility.Visible : Visibility.Collapsed) : Visibility.Collapsed;
}