如何检测整个绑定的 ViewModel 是否已更改?

How to detect that entire binded ViewModel has changed?

我有一个简单的视图模型 class,它包含三个属性。

public class ColorViewModel : INotifyPropertyChanged
{
    private int red;
    public int Red
    {
        get { return red; }
        set
        {
            if (red != value)
            {
                red = value;
                RaisePropertyChanged("Red");
            }
        }
    }

    private int green;
    public int Green
    {
        get { return green; }
        set
        {
            if (green != value)
            {
                green = value;
                RaisePropertyChanged("Green");
            }
        }
    }

    private int blue;
    public int Blue
    {
        get { return blue; }
        set
        {
            if (blue!= value)
            {
               blue = value;
               RaisePropertyChanged("Blue");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };
    private void RaisePropertyChanged(string propName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
}

现在我想将整个模型传递给给定的转换器,以便为 RGB 值创建颜色。

<Rectangle Grid.Row="4" Grid.Column="1" HorizontalAlignment="Center" Height="120" Width="120"
    Fill="{Binding Model, Converter={StaticResource intToBrushValueConverter}}"/>

其中 Model 是我的 ColorViewModel.

的一个实例

问题是 intToBrushValueConverter 转换器只在程序启动时触发一次。换句话说,当 Model 改变时,转换器不会被触发。

然后当 ColorViewModel 中的一个 属性 被更改时,PropertyChanged 工作,但 initToBrushValueConverter 转换器没有命中。

有一种方法可以解决这个问题吗?

我正在寻找一种不使用 MultiBindingCommandParameter 的解决方案。

提前致谢。

问题在于 Rectangle 仅侦听 ModelPropertyChanged 事件,而不侦听三种颜色属性。

尝试使用 RaisePropertyChanged("Model");RaisePropertyChanged(null);Model 本身引发事件。

挂钩 属性 将事件更改为父视图模型,并在火 属性 每当 属性 更改颜色 属性 时更改事件。

public class ParentViewModel : INotifyPropertyChanged
{
    private ColorViewModel color;
    public ColorViewModel Color
    {
        get { return color; }
        set
        {
            if (color != value)
            {
                if (color != null)
                    color.PropertyChanged -= this.ChildPropertyChanged;

                color = value;

                if (color != null)
                    color.PropertyChanged += this.ChildPropertyChanged;

                RaisePropertyChanged("Color");
            }
        }
    }

    private void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if(Color == sender)
        {
            RaisePropertyChanged("Color");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };
    private void RaisePropertyChanged(string propName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
}

更新:

下面是完整的工作解决方案。

MainWindows.xaml.cs

using System;
using System.Windows;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private Random random = new Random();
        private ParentViewModel model;
        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = this;
            this.model = new ParentViewModel();
            this.model.Color = new ColorViewModel();
        }

        public ParentViewModel Model
        {
            get { return model; }
            set
            {
                model = value;
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.model.Color.Blue = (byte)random.Next(0, 255);
            this.model.Color.Green = (byte)random.Next(0, 255);
            this.model.Color.Red = (byte)random.Next(0, 255);
        }
    }
}

MainWindow.xaml

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:IntToBrushConverter x:Key="intToBrushConverter" />
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="50" />
        </Grid.RowDefinitions>
        <Rectangle HorizontalAlignment="Center" Height="120" Width="120" Fill="{Binding Model.Color, Converter={StaticResource intToBrushConverter}, FallbackValue=Black}"/>
        <Button Grid.Row="1" Content="Click me!" Click="Button_Click" />
    </Grid>
</Window>

IntToBrushConverter.cs

using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;

namespace WpfApp1
{
    public class IntToBrushConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if(value is ColorViewModel)
            {
                var color = value as ColorViewModel;
                return new SolidColorBrush(Color.FromRgb(color.Red, color.Green, color.Blue));
            }


            throw new NotSupportedException();
        }

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

ParentViewModel.cs

using System.ComponentModel;

namespace WpfApp1
{
    public class ParentViewModel : INotifyPropertyChanged
    {
        private ColorViewModel color;
        public ColorViewModel Color
        {
            get { return color; }
            set
            {
                if (color != value)
                {
                    if (color != null)
                        color.PropertyChanged -= this.ChildPropertyChanged;

                    color = value;

                    if (color != null)
                        color.PropertyChanged += this.ChildPropertyChanged;

                    RaisePropertyChanged("Color");
                }
            }
        }

        private void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (Color == sender)
            {
                RaisePropertyChanged("Color");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };
        private void RaisePropertyChanged(string propName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
}

ColorViewModel.cs

using System.ComponentModel;

namespace WpfApp1
{
    public class ColorViewModel : INotifyPropertyChanged
    {
        private byte red;
        public byte Red
        {
            get { return red; }
            set
            {
                if (red != value)
                {
                    red = value;
                    RaisePropertyChanged("Red");
                }
            }
        }

        private byte green;
        public byte Green
        {
            get { return green; }
            set
            {
                if (green != value)
                {
                    green = value;
                    RaisePropertyChanged("Green");
                }
            }
        }

        private byte blue;
        public byte Blue
        {
            get { return blue; }
            set
            {
                if (blue != value)
                {
                    blue = value;
                    RaisePropertyChanged("Blue");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };
        private void RaisePropertyChanged(string propName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
}

App.xaml 和 App.xaml.cs 是模板生成的标准。

输出

希望对您有所帮助!