INotifyPropertyChanged绑定未按预期更新

时间:2011-10-26 05:49:18

标签: c# silverlight mvvm

所以这就是我正在反对的问题:我有一个自定义用户控件,它公开了两个绑定到我的ViewModel的依赖项属性。在我的ViewModel中,我有一个类的实例,它包含多个属性,这些属性表示与用户控件相关的值以及控制操作的项。这里有一些示例代码可以直观地解释它,所以这里是我控件的一个简单示例,它是一个Slider,它与一个允许用户锁定滑块的复选框相结合。

    <custom:SliderControl IsLocked="{Binding Path=CustomClass.IsLocked, Mode=TwoWay}" SliderValue="{Binding Path=CustomClass.Value, Mode=TwoWay}" />

IsLocked和SliderValue是依赖项属性,可以有效地操作自定义控件中包含的复选框和滑块。除了绑定到我定义的类之外,所有控制函数都按预期工作。如果我创建单个属性,如在一个int属性和一个bool属性中,绑定按预期工作。但是我有五个滑块,我实际代码中的每个滑块都有五个与之相关的属性。我试图通过创建一个类来消除代码重复,以便在可重用的对象中保存这些属性,将我的25个属性缩小到5个类实例。

我的CustomClass继承了ObservableObject,并且分别具有名为IsLocked和SliderValue的bool属性和int属性。对于更多的视觉辅助,这里是它的样子:

    public class CustomClass : ObservableObject
    {
        public const string SliderValuePropertyName = "SliderValue";
        private int _sliderValue= 0;
        public int SliderValue
        {
            get
            {
                return _sliderValue;
            }

            set
            {
                if (_sliderValue== value)
                {
                    return;
                }


                _sliderValue= value;
                RaisePropertyChanged(SliderValuePropertyName );
            }
        }

    public const string IsCheckedPropertyName = "IsChecked";
    private bool _isChecked = false;
    public bool IsChecked
    {
        get
        {
            return _isChecked;
        }

        set
        {
            if (_isChecked == value)
            {
                return;
            }
            _isChecked = value;
            RaisePropertyChanged(IsCheckedPropertyName);
        }
    }

ViewModel属性非常相似,看起来像这样,在ViewModel加载时会创建一个新的类实例:

    public const string SliderOnePropertyName = "SliderOne";
    private CustomClass _sliderOne;
    public CustomClass SliderOne
    {
        get
        {
            return _sliderOne;
        }

        set
        {
            if (_sliderOne== value)
            {
                return;
            }

            _sliderOne= value;
            RaisePropertyChanged(SliderOnePropertyName );
        }
    }

为什么绑定到类中属性的依赖项属性的更新不能正确更新?是因为您无法自行更新类实例属性,而是必须在发生更改时更新整个类实例?或者我是否需要在此ViewModel属性中进一步自定义setter?因为它现在正在改变滑块值或复选框永远不会点击绑定属性,并且在调试时没有任何错误。

编辑:我还在Border中包围了控件,并将Border UIElement的DataContext设置为类的DataContext,然后将更简单的路径绑定应用于底层自定义控件。然而,这对我的问题没有任何影响。

我是一个本土的程序员,所以我经常在把代码放在一起时会错过任何东西,我猜这是这种情况,除非我正在尝试的东西不起作用。

非常感谢任何帮助。

编辑:所以我一直在使用自定义事件,让我知道自定义控件的特定属性何时更改,然后在我的ViewModel中连接该事件以更新现有类。这仍然有效,但仍然会创建代码重复,因为现在我必须有10个事件,每个控件有2个事件,一个用于检查滑块值何时更改,另一个用于检查复选框IsChecked值何时更改。存在此代码重复,因为您无法路由多个命令参数(例如正在操作滑块的简单字符串标识符以及您要在代码中使用的值)。这种限制意味着我不能只使用2个事件来区分在定义的方法中哪个控件正在发生变化,因为将物理控件暴露给ViewModel会破坏MVVM模式。使用类作为用户控件的datacontext使得我不关心操作什么控件,因为他们每个都有自己的类实例。使用事件会解开MVVM模式,因为现在我需要知道用户正在操纵五个控件中的哪一个。

在属性绑定中使用类不是很难。我不得不遗漏一些补救措施。

3 个答案:

答案 0 :(得分:1)

可能只是语法错误。试试这个

{Binding Path = CustomClass.IsLocked,Mode = TwoWay}

答案 1 :(得分:1)

这是一个完整的例子:

public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();
        this.DataContext = new ViewModel();
    }
}


public class ViewModel
{
    public SliderValues slv { get; private set; }

    public ViewModel()
    {
        slv = new SliderValues();
    }
}

public class SliderValues : INotifyPropertyChanged
{
    bool _isLocked = false;

    public bool IsLocked
    {
        get { return _isLocked; }
        set
        {
            _isLocked = value;
            OnPropertyChanged("IsLocked");
        }

    }
    int _theValue = 5;

    public int TheValue
    {
        get { return _theValue; }
        set
        {
            _theValue = value;
            OnPropertyChanged("TheValue");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string prop)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(prop));

    }
}

现在是xaml:

<UserControl x:Class="TestBindings.MainPage"
    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"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Slider Height="23" HorizontalAlignment="Left" Margin="114,138,0,0" Name="slider1" VerticalAlignment="Top" Width="100"
                DataContext="{Binding slv}" Value="{Binding TheValue, Mode=TwoWay}"/>
    </Grid>
</UserControl>

答案 2 :(得分:0)

试试这个...... <custom:SliderControl DataContext="{Binding CustomClass}" IsLocked="{Binding IsLocked, Mode=TwoWay}" SliderValue="{Binding Value, Mode=TwoWay}" />