【问题标题】:INotifyPropertyChanged binding not updating as expectedINotifyPropertyChanged 绑定未按预期更新
【发布时间】:2011-10-26 05:49:17
【问题描述】:

所以这是我正在努力解决的问题:我有一个自定义用户控件,它公开了两个绑定到我的 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 属性中的设置器?因为它现在更改滑块值或复选框根本不会触及绑定属性,调试时也不会出现任何错误。

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

我是一名土生土长的程序员,所以在将代码放在一起时我经常会错过一些事情,我猜这里就是这种情况,除非我尝试的方法不起作用。

任何帮助将不胜感激。

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

在属性绑定中使用类不会这么难。我必须错过一些补救措施。

【问题讨论】:

  • 您是否检查了输出窗口的绑定错误?
  • 是的,“输出”窗口中完全没有错误,调试完全没有问题。我相信这在 XAML 中使用绑定时很常见,如果绑定无效,如果属性可以使用默认值初始化,它永远不会报告问题。
  • 视图设置的DataContext 是什么?
  • 视图的DataContext设置为分配给页面的ViewModel。
  • 在您的 SliderControl 用户控件上再次设置数据上下文

标签: c# silverlight mvvm


【解决方案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>

【讨论】:

  • 这正是我现在所处的位置。我为自己创建的问题是我在一个单独的文件中定义了类,并认为我需要为在我的 ViewModel 中引发 INotifyPropertyChanged 的​​类实例创建一个属性,这掩盖了类代码中发生的一切并忽略我创建的属性。这使得依赖于知道类属性何时更改的方法难以执行。我正在通过使用 MVVM Light 消息传递来解决这个问题。感谢您提供此代码示例!
  • 我有在操作任何单个滑块控件时执行的方法的原因是它们具有相互依赖性并且都与共享的最大设置有关,如果达到该设置,则将开始从滑块控件中浸出最高值。这造成了一种情况,我需要在操纵时所有控件,然后检查所有其他控件并在超出最大值时创建奇偶校验。
【解决方案2】:

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

{绑定路径=CustomClass.IsLocked, Mode=TwoWay}

【讨论】:

  • 对不起,我打出的例子确实有多个拼写错误,但实际代码没有。我现在已经修好了。
【解决方案3】:

试试这个...&lt;custom:SliderControl DataContext="{Binding CustomClass}" IsLocked="{Binding IsLocked, Mode=TwoWay}" SliderValue="{Binding Value, Mode=TwoWay}" /&gt;

【讨论】:

    猜你喜欢
    • 2019-04-23
    • 2016-12-08
    • 1970-01-01
    • 2020-09-15
    • 2011-03-05
    • 2021-11-22
    • 2018-08-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多