仅当它是所需类型时才绑定到接口(否则使用回退)?
Posted
技术标签:
【中文标题】仅当它是所需类型时才绑定到接口(否则使用回退)?【英文标题】:Binding to interface only if it is the needed type (use fallback otherwise)? 【发布时间】:2021-12-08 22:33:17 【问题描述】:我的 ViewModel 中有以下属性
public IEquipment Equipment
get
return equipment;
set
if (equipment != value)
equipment = value;
InvokePropertyChanged("Equipment");
该项目本身有一个bool
属性,它绑定到我的视图中的一个Ellipse
,我想将其用作指示器项目:
<Ellipse Width="10" Height="10" Fill="Binding Equipment.IsAvailable, Converter=StaticResource BoolToColorConverter, FallbackValue=DarkGray" Margin="1"/>
BoolToColorConverter
只是将颜色转换为绿色 (true
) 或红色 (false
)。在运行时Equipment
可以是继承自IEquipment
的两种类类型之一的实例。其中只有一个具有IsAvailable
属性。在实践中,这很好用,我得到了红色或绿色的颜色……或者灰色的颜色,以防其他类型的设备处于活动状态。
问题是,每次 GUI 更新时,都会输出以下警告:
System.Windows.Data 警告:40:BindingExpression 路径错误:在“对象”上找不到“IsAvailable”属性
如何避免这个问题?基本上我只想绑定到这个属性,如果它是正确的类型。 我可以想到两个我不是特别喜欢的解决方案:
-
只需将
IsAvailable
属性添加到其他类型并将其设置为null(BoolToColorConverter
可以处理空值并返回深灰色):这对于简单的bool
来说可能没问题,但在我的实际情况中是其他项目,它们是特定于类的。
在代码隐藏中进行数据绑定:这可能有效。在启动时使用Loaded
之类的事件在运行时根据类型手动设置绑定。但是,这对于以后的调试可能会很麻烦,因为项目中的所有其他 Bindings 都直接发生在 xaml 文件中。此外,Equipment
可能会在 ViewModel 的生命周期内发生变化,因此我必须以某种方式对其进行跟踪。
【问题讨论】:
您可以将类似IsEquipmentAvailable
的属性添加到模型本身。在 getter 中,您检查 Equipment 是否具有 IsAvailable 属性(最好使用某些接口而不是通过反射)。然后你直接绑定到这个属性而不是Equipment.IsAvailable
。
@Evk 谢谢,这也是我的第一个想法,但是如果没有设置器,GUI 会有新的更新。实现 setter 并跟踪更改将意味着我不想拥有依赖项......
如果您愿意,您应该不需要 setter: 1. 从 Equipment
的 setter 调用 InvokePropertyChanged("IsEquipmentAvailable")
2. 在其 setter 中订阅 Equipment 的 propertychanged 并在那里执行相同的操作。一些代码,但可能比使用数据模板重新设计更容易。
@Evk Ha,当然!谢谢你没有想到......唯一让我烦恼的事情与我的 OP 中的第一点相同:如果它不仅仅是一个简单的布尔值,而是列表、对象等。我的 ViewModel 中突然出现了不相关的内容。所以我想这个解决方案很好,但要视情况而定。
【参考方案1】:
Xaml 不绑定到接口,它绑定到具体类型。
如果你的类型有不同的属性,那么你需要不同的 xaml 来绑定它们。
使用DataTemplates 指定不同的xaml 来显示每种类型。
【讨论】:
这在很大程度上取决于您要做什么。例如,您可以将您的 IEquipment 对象包装在一个始终具有 IsAvailable 属性的 EquipmentViewModel 中,并处理其 getter 中的差异。您的 “还有其他项目,它们是特定类别的。” 引用让我认为与您所展示的内容存在更根本的差异。 是的,正如上面提到的在与 Evk 的 cmets 中我们讨论的一样。但是,就我而言,我必须使用模板,因为我有几个不同的属性。【参考方案2】:如果IEquipment
的派生类(此处以Equipment
和OtherEquipment
为例)的属性差异很大并且不共享一个通用接口,则它们很可能在外观上有所不同。在这种情况下,每种类型都需要不同的DataTemplate
s。这是ContentControl
的一个示例,但它与ItemsContol
s 的工作方式相同,它带有自动应用的隐式数据模板(不是x:Key
,而是DataType
)。
<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>
针对您的特定问题的解决方法可能是编写自定义的专用值转换器。
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"/>
【讨论】:
以上是关于仅当它是所需类型时才绑定到接口(否则使用回退)?的主要内容,如果未能解决你的问题,请参考以下文章
Javascript:仅当它是文本的第一个单词时才替换“x”,而不是其他任何地方