首页 文章

属性变化的多重颜色变化 - 如何实用地清除?

提问于
浏览
0

编辑:我创建了一个示例项目,显示我已完成的工作和不工作的工作 . https://github.com/jmooney5115/clear-multibinding

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

我有完美的代码,可以使用MultiBinding和值转换器来改变颜色 . 问题是我无法弄清楚如何在我的代码中调用Save()后重置后台 . 我已经尝试过DataContext = null然后DataContext = this但控件闪烁 . 一定有更好的方法 .

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

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

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

BackgroundColorConverter.cs

/// <summary>
/// https://stackoverflow.com/questions/1224144/change-background-color-for-wpf-textbox-in-changed-state
/// 
/// 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();
    }
}

Updates

编辑:这是数据网格单元的多重绑定 . 如果多重绑定转换器返回true,请将背景颜色设置为LightBlue . 如果为false,则背景为默认颜色 .

<DataGrid.Columns>
    <DataGridTextColumn Header="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>

我做了这个方法来保存后重置对象的绑定 .

/// <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();
        }
    }
}

Final Update

这个问题解答了如何在DataGridCell上使用MultiBinding:Update MultiBinding on DataGridCell

2 回答

  • 1

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

    无论如何,既然你继承了这个应用程序,你必须考虑转换整个代码所需的时间,有时候是不可能的 . 因此,在这种情况下,您可以在保存数据时强制每个单个多重绑定“刷新” .

    我们假设这是你的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 实现 . 我希望它可以帮到你 .

  • 1

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

    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));
        }
    }
    

    Converter:

    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}}" />
    

相关问题