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

时间:2018-01-11 14:05:32

标签: c# wpf mvvm binding model-binding

我有一个简单的viewmodel类,它包含三个属性。

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的情况下搜索一个解决方案。

提前致谢。

2 个答案:

答案 0 :(得分:1)

当属性在Color属性上发生更改时,Hook属性将事件更改为父视图模型和fire属性更改事件。

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是模板生成的标准。

输出

Initial window

Random color 1

Random color 2

希望它有所帮助!

答案 1 :(得分:0)

问题是Rectangle只侦听PropertyChanged的{​​{1}}事件,而不是三种颜色属性。

尝试使用ModelModelRaisePropertyChanged("Model");本身举办活动。