属性 变化时 MultiBinding 颜色变化 - 如何务实地清除?

MultiBinding Color Change on Property Change - How to Clear pragmatically?

编辑:我创建了一个示例项目,显示了我所做的和不起作用的。 https://github.com/jmooney5115/clear-multibinding

我有一个带有控件(文本框、数据网格等)的 WPF 应用程序。当控件上的值发生变化时,我需要通过更改背景颜色来指示它。保存更改后,背景颜色需要返回到未更改的状态,而无需重新加载控件。这个应用不是MVVM,不要判断我继承了它

我的代码非常适合使用 MultiBinding 和值转换器更改颜色。问题是我不知道如何在我的代码中调用 Save() 后重置背景。我试过做 DataContext = null 然后做 DataContext = this 但控件闪烁。必须有更好的方法。

问:如何在不重新加载控件的情况下将背景重置为未更改状态?

MultiBinding XAML - 这通过将字符串 [] 传递给 BackgroundColorConverter 来实现。 string[0] 是 OneTime 绑定。 string1 是另一个绑定。

<TextBox.Background>
    <MultiBinding Converter="{StaticResource BackgroundColorConverter}">
        <Binding Path="DeviceObj.Name" />
        <Binding Path="DeviceObj.Name" Mode="OneTime" />
    </MultiBinding>
</TextBox.Background>

BackgroundColorConverter.cs

/// <summary>
/// 
/// 
/// Property changed
/// </summary>
public class BackgroundColorConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var colorRed = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("#FFB0E0E6");
        var colorWhite = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString("White");

        var unchanged = new SolidColorBrush(colorWhite);
        var changed = new SolidColorBrush(colorRed);

        if (values.Length == 2)
            if (values[0].Equals(values[1]))
                return unchanged;
            else
                return changed;
        else
            return changed;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

更新

编辑:这是数据网格单元格的多重绑定。如果多绑定转换器 returns 为真,则将背景颜色设置为浅蓝色。如果为 false,则背景为默认颜色。

<DataGrid.Columns>
    <DataGridTextColumn Header="Name" Binding="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
        <!--  -->
        <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>

我制作这个方法是为了在保存后重置对象的绑定。

/// <summary>
/// Update the data binding after a save to clear the blue that could be there when
/// a change is detected.
/// </summary>
/// <typeparam name="T">Type to search for</typeparam>
/// <param name="parentDepObj">Parent object we want to reset the binding for their children.</param>
public static void UpdateDataBinding<T>(DependencyObject parentDepObj) where T : DependencyObject
{
    if (parentDepObj != null)
    {
        MultiBindingExpression multiBindingExpression;

        foreach (var control in UIHelper.FindVisualChildren<T>(parentDepObj))
        {
            multiBindingExpression = BindingOperations.GetMultiBindingExpression(control, Control.BackgroundProperty);
            if (multiBindingExpression != null)
                multiBindingExpression.UpdateTarget();
        }
    }
}

最终更新

这个问题回答了如何在 DataGridCell 上为我的目的使用 MultiBinding:

如果 Name 或其他内容已更改,您必须将 bool Saved 属性 粘贴到 DeviceObj 并处理它。

ViewModel:

public class Device : INotifyPropertyChanged
{
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            if (value != _name)
            {
                _name = value;
                Saved = false;
                NotifyPropertyChanged(nameof(Name));
            }
        }
    }
    private string _name;


    public bool Saved
    {
        get
        {
            return _saved;
        }
        set
        {
            if (value != _saved)
            {
                _saved = value;
                NotifyPropertyChanged(nameof(Saved));
            }
        }
    }
    private bool _saved = true;

    public void Save()
    {
        //Saving..
        Saved = true;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void NotifyPropertyChanged(string info)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
    }
}


转换器:

public class BoolToSolColBrushConverter : IValueConverter
{
    private static SolidColorBrush changedBr = new SolidColorBrush(Colors.Red);
    private static SolidColorBrush unchangedBr = new SolidColorBrush(Colors.Green);
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        try
        {
            if ((bool)value)
            {
                return unchangedBr;
            }

        }
        catch (Exception)
        {
        }
        return changedBr;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }


XAML:

<TextBox Text="{Binding Name}" Background="{Binding Saved, Converter={StaticResiurce BoolToSolColBrushConverter}}" />

IHMO MVVM 解决方案(如 Rekshino 所提议)肯定比非 MVVM 解决方案更好。视图模型应该注意跟踪修改后的数据。

反正你继承了这个应用程序,你就得考虑转换整个代码需要多少时间,有时是不可能的。因此,在这种情况下,您可以在保存数据时强制每个多重绑定到 "refresh"。

假设这是您的 XAML(有两个或更多 TextBoxes):

<StackPanel>
    <TextBox Margin="5" Text="{Binding DeviceObj.Name, Mode=TwoWay}">
        <TextBox.Background>
            <MultiBinding Converter="{StaticResource BackgroundColorConverter}">
                <Binding Path="DeviceObj.Name" />
                <Binding Path="DeviceObj.Name" Mode="OneTime" />
            </MultiBinding>
        </TextBox.Background>
    </TextBox>

    <TextBox Margin="5" Text="{Binding DeviceObj.Surname, Mode=TwoWay}">
        <TextBox.Background>
            <MultiBinding Converter="{StaticResource BackgroundColorConverter}">
                <Binding Path="DeviceObj.Surname" />
                <Binding Path="DeviceObj.Surname" Mode="OneTime" />
            </MultiBinding>
        </TextBox.Background>
    </TextBox>

    <Button Content="Save" Click="Button_Click" Margin="5,10,5,10" />
</StackPanel>

当您单击 "Save" Button 时,您可以强制 MultiBindings 以这种方式更新它们自己的目标:

private void Button_Click(object sender, RoutedEventArgs e)
{
    MultiBindingExpression multiBindingExpression;

    foreach (TextBox textBox in FindVisualChildren<TextBox>(this))
    {
        multiBindingExpression = BindingOperations.GetMultiBindingExpression(textBox, TextBox.BackgroundProperty);
        multiBindingExpression.UpdateTarget();
    }
}

您可以在 this answer 中找到 FindVisualChildren 实现。希望能帮到你。