C# WPF IValueConverter更改属性:如何强制更新?

C# WPF IValueConverter更改属性:如何强制更新?,c#,wpf,data-binding,C#,Wpf,Data Binding,在MVVM模型中,我的视图使用值转换器。此值转换器具有影响转换值的特性X。此属性X可能会更改 每当X发生更改时,使用此值转换器转换的所有值都需要更新。当然,我的ViewModel不知道我的视图使用了这个转换器,所以它不能通知PropertyChanged。此外,我认为让ViewModel知道值转换为什么格式是不整洁的 我的值转换器不知道它用于哪些值。幸运的是,我的XAML及其代码隐藏类做到了这一点 因此,我的视图有两个转换器作为资源,两个文本块使用这些资源: MyView.XAML: <U

在MVVM模型中,我的视图使用值转换器。此值转换器具有影响转换值的特性X。此属性X可能会更改

每当X发生更改时,使用此值转换器转换的所有值都需要更新。当然,我的ViewModel不知道我的视图使用了这个转换器,所以它不能通知PropertyChanged。此外,我认为让ViewModel知道值转换为什么格式是不整洁的

我的值转换器不知道它用于哪些值。幸运的是,我的XAML及其代码隐藏类做到了这一点

因此,我的视图有两个转换器作为资源,两个文本块使用这些资源:

MyView.XAML:

<UserControl.Resources>
    <convert:FormattedStringConverter x:Key="SelectedHistoryConverter" />
    <vm:TimeFrameConverter x:Key="TimeFrameConverter"/>
</UserControl.Resources>

...

<TextBlock Height="20" Name="HistoryTime"
           Text="{vm:CultureInfoBinding Path=SelectedHistoryTime,
           Converter= {StaticResource SelectedHistoryConverter},
           ConverterParameter='History Time: {0:G}'}"/>

<TextBlock Height="20" Name="Timeframe"
           Text="{vm:CultureInfoBinding Path=Timeframe,
           Converter= {StaticResource TimeFrameConverter},
           ConverterParameter='Time Frame: [{0:G}, {1:G}]'}"/>
在UpdateTargets中,我需要告诉两个TextBlock更新它们的值,这将使用更改的转换器

为此,我在文章中使用了公认的答案

这个很好用。然而,这意味着每当我在XAML中添加使用此绑定的元素时,我都必须将此元素添加到UpdateTargets

有没有办法让从绑定派生的类知道哪些目标绑定到它

每当X发生更改时,使用此值转换器转换的所有值都需要更新

在这种情况下,代替转换器,可能让VM通过控件随后将绑定到的按需属性提供计算值


从你的例子中借用一下,我有三个属性,两个是计算出来的(它们完成转换器的工作),一个是计算出来的属性所利用的现有属性

计算的

public string HistoryTime { get { return SelectedHistoryTime.AddDays(-2).ToShortDate(); } }
public string Timeframe { 
      get 
      {
         return $"Time Frame: [{SelectedHistoryTime.AddDays(-14)}, {SelectedHistoryTime.AddDays(+14).ToShortDate()}]"; 
      }
 }
现有通知所有

然后,每当
HistoryTime
TimeFrame
使用的所有相应属性在其设置器中通知更改,例如

public DateTime SelectedHistoryTime 
{ 
    get { return _SelectedHistoryTime; }
    set
       {
         _SelectedHistoryTime = value;
         NotifyChange("SelectedHistoryTime");

         // These rely on this property, so they change too
         NotifyChange("HistoryTime");
         NotifyChange("Timeframe");
       }
}

在WPF绑定系统中,最好允许系统响应与绑定值(属性)直接关联的通知。正如您所发现的,可以显式刷新绑定。但这是一种容易出错、效率低下的解决问题的方法

如前所述,最好的方法是编写一个
IMultiValueConverter
。这允许通过
INotifyPropertyChanged
事件绑定每个源属性,与您可能绑定的任何其他属性一样,与绑定系统顺利工作。这样,主源属性和“属性X”都将自动调用转换器,如果其中一个发生更改,则根据需要更新目标属性

正如另一个答案所建议的,也可以在视图模型中编写复合属性。但我倾向于尽可能多地声明面向视图的代码,而不是将命令式实现放在代码后面。这提供了最佳的灵活性,并且更好地遵循了“关注点分离”原则(即,通过隔离转换器本身的“数据转换”方面,而不是用该逻辑重载视图模型)


关于如何实现
IMultiValueConverter
,这里有很多例子,如Stack Overflow、Microsoft的文档网站,当然还有各种web教程。我相信您将能够轻松地应用您已经了解的XAML绑定语法、值转换器等,使用
IMultiValueConverter

而不是带有X属性的转换器来实现当前场景,在视图模型中使用绑定到X源的多重绑定。绑定到X意味着将我的转换器更改为多重转换器,其中一个值的值为X。这是可行的,我尝试过,但我认为这会让我的转换器神奇地工作:值[0]和值[1]是要转换的值;值[2]=x,不使用。若XAML改变了顺序,那个么我的代码就不能再工作了。别人很难发现错误。绑定对象知道其目标和用于此目标的转换器,因此我认为最好让绑定对象通知其目标,当您将属性传递给转换器时,它必须更新
{Binding MyProperty,Converter=MyValueConverter}
,当绑定属性提升其
PropertyChanged
时,控件更新其依赖关系。就像没有转换器一样简单。如果您使用
IMultiValueConverter
,它将更新传递到转换器的绑定的每个
属性更改。(根据您的顾虑,转换器将按照与您传递到的相同顺序接收
对象[]值
),因此,您可以尝试使用多值转换器或多绑定。
public string HistoryTime { get { return SelectedHistoryTime.AddDays(-2).ToShortDate(); } }
public string Timeframe { 
      get 
      {
         return $"Time Frame: [{SelectedHistoryTime.AddDays(-14)}, {SelectedHistoryTime.AddDays(+14).ToShortDate()}]"; 
      }
 }
public DateTime SelectedHistoryTime 
{ 
    get { return _SelectedHistoryTime; }
    set
       {
         _SelectedHistoryTime = value;
         NotifyChange("SelectedHistoryTime");

         // These rely on this property, so they change too
         NotifyChange("HistoryTime");
         NotifyChange("Timeframe");
       }
}