仅当它是所需类型时才绑定到接口(否则使用回退)?

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 的派生类(此处以EquipmentOtherEquipment 为例)的属性差异很大并且不共享一个通用接口,则它们很可能在外观上有所不同。在这种情况下,每种类型都需要不同的DataTemplates。这是ContentControl 的一个示例,但它与ItemsContols 的工作方式相同,它带有自动应用的隐式数据模板(不是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”,而不是其他任何地方

仅当它在 AngularJs 中有值时才需要下拉

仅当它在 Pig 中的内部引号(“”)时才替换逗号(,)

添加 www 的 htaccess 条件。仅当它在 url 中不包含字符串时才到 url

如何使用泛型类型接口在接口内使参数有条件

仅当它不存在时才在 SQLite 中创建表