WPF IValueConverter меняет свойство: как форсировать обновление? - PullRequest
0 голосов
/ 27 февраля 2020

В модели 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}]'}"/>

Обработчик событий в MyView.XAML.cs

private void OnMyParameterChanged(object sender, EventArgs e)
{
    UpdateConverterParameters(); // updates the changed property in the converters
    UpdateTargets();             // forces an Update of all Targets that use these converters
}

В UpdateTargets мне нужно указать двум TextBlocks обновить их значения, которые будут использовать измененные конвертеры.

Для этого я использовал принятый ответ в StackOverflow: Как заставить привязку WPF к refre sh?

public void UpdateTargets()
{
    BindingExpression historyTimeExpression = HistoryTime.GetBindingExpression(TextBlock.TextProperty);
    historyTimeExpression.UpdateTarget();

    BindingExpression timeframeExpression = Timeframe.GetBindingExpression(TextBlock.TextProperty);
    timeframeExpression .UpdateTarget();
}

Это прекрасно работает. Однако это означает, что всякий раз, когда я добавляю элемент в XAML, который использует эту привязку, мне придется добавлять этот элемент в UpdateTargets.

Есть ли способ для класса, производного от Binding, узнать, какие цели связаны с ним

Ответы [ 2 ]

1 голос
/ 28 февраля 2020

Всякий раз, когда X изменяется, все значения, которые были преобразованы с помощью этого преобразователя значений, должны обновляться.

В этой ситуации вместо преобразователя виртуальная машина может предоставить вычислено значений через свойства по требованию, с которыми впоследствии будут связаны элементы управления.


Итак, чтобы немного позаимствовать из вашего примера, у меня есть три свойства, два вычисляются (которые выполняют работу преобразователя (ей)), и одно - это существующее, которое используют вычисленные свойства.

Вычислено

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");
       }
}
0 голосов
/ 28 февраля 2020

В системе привязки WPF лучше всего разрешить системе отвечать на уведомления, непосредственно связанные со связанными значениями (свойствами). Как вы обнаружили, можно явно переопределить привязки sh. Но это склонный к ошибкам неэффективный способ решения проблемы.

Как объяснено здесь , лучший способ сделать это - написать IMultiValueConverter вместо этого. Это позволяет привязывать исходные свойства к каждому событию INotifyPropertyChanged, беспрепятственно работая с системой привязки, как и любые другие свойства, которые вы можете связать. Таким образом, как основное свойство источника, так и "свойство X" будут автоматически вызывать конвертер, обновляя целевое свойство по мере необходимости, если любое из них изменяется.

Также возможно напишите составное свойство в вашей модели представления, как подсказывает другой ответ. Но я предпочитаю объявлять как можно большей части ориентированного на представление кода, а не помещать императивную реализацию в коде позади. Это обеспечивает лучшую гибкость, а также лучше следует философии «разделения интересов» (т. Е. Изолируя аспект «преобразования данных» в самом преобразователе, а не перегружая модель представления этой логикой * 1023). *).

Существует множество примеров того, как реализовать IMultiValueConverter, здесь, на Stack Overflow, на веб-сайте документации Microsoft и, конечно, в различных учебных пособиях в Интернете. Я надеюсь, что вы сможете легко применить то, что вы уже знаете о синтаксисе привязки XAML, преобразователях значений и т. Д. c. реализовать свой текущий сценарий, используя IMultiValueConverter.

...