I am integrating property change detection in an application by way of MultiBinding
and IMultiValueConverter
. So when a user makes changes to a 'DataGrid', the DataGridCell' changes background color. My issue is that when the user saves their work I cannot remove the changed background without a screen flicker. I can do
DataContext = nullthen
DataContext = this` but it causes a screen flicker. I cannot call an update binding to reset the MultiBinding to default.
Q: How can I update the MultiBinding on a DataGridCell like below?
Unfortunately this is not MVVM. I created a project that shows the issue: https://github.com/jmooney5115/clear-multibinding
This solution works for TextBox but not DataGridCell:
foreach (TextBox textBox in FindVisualChildren<TextBox>(this))
{
multiBindingExpression = BindingOperations.GetMultiBindingExpression(textBox, TextBox.BackgroundProperty);
multiBindingExpression.UpdateTarget();
}
This is the multi binding for a data grid cell The multi value converter takes the original value and the modified value. If the value is changed it returns true to set the background color to LightBlue. If false, the background is the default color.
<DataGrid.Columns>
<DataGridTextColumn Header="Destination Tag name" Binding="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<!-- https://stackoverflow.com/questions/5902351/issue-while-mixing-multibinding-converter-and-trigger-in-style -->
<DataGridTextColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource BackgroundColorConverterBool}">
<Binding Path="Name" />
<Binding Path="Name" Mode="OneTime" />
</MultiBinding>
</DataTrigger.Binding>
</DataTrigger>
<Setter Property="Background" Value="LightBlue"></Setter>
</Style.Triggers>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
Here is the multi value converter I'm using:
/// <summary>
/// https://stackoverflow.com/questions/1224144/change-background-color-for-wpf-textbox-in-changed-state
///
/// Property changed and display it on a datagrid.
///
/// Boolean Converter
/// </summary>
public class BackgroundColorConverterBool : IMultiValueConverter
{
/// <summary>
///
/// </summary>
/// <param name="values"></param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns>True is property has changed</returns>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] is null || values[1] is null) return false;
if (values.Length == 2)
if (values[0].Equals(values[1]))
return false;
else
return true;
else
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Update
Using the solution marked as my answer I was able to expand on it to generalize the UpdateState() method.
foreach (var prop in this.GetType().GetProperties())
_memo[prop.Name] = prop.GetValue(this);
I wouldn't rely on OneTime binding mode and tricks with clearing binding to track changes in data. Implement something like Memento pattern instead.
Add this code which stores state in Item class:
to make indexer
this[]
work you have to rename class (e.g. toItemVm
), because class name and member name can't be the same, and .NET uses"Item"
as indexer property name.note that notifications for indexer have
"Item[]"
format, andVerifyProperty()
method should be fixed too:now, to use unmodified value in window, bind to indexer like this:
save initial state when creating items:
and save changes in state on button click: