【问题标题】:WPF - How to keep Dependency Property and View Model property in sync?WPF - 如何保持依赖属性和视图模型属性同步?
【发布时间】:2017-08-28 23:17:17
【问题描述】:

在 WPF 中,我一直试图弄清楚如何保持视图依赖属性和其中一个视图模型的属性同步一段时间,但没有任何运气。我对该主题进行了相当多的研究,但没有一个建议的解决方案对我有用,我希望有人可以帮助我找到我所缺少的。

我尝试了这篇帖子中建议的许多事情,Twoway-bind view's DependencyProperty to viewmodel's property?,因为我读过的所有内容看起来都是最有希望的,但始终无法获得我想要的结果。

我编写了一个简单的程序来演示我遇到的问题。在其中,我将 MainWindowViewModel 中的属性 IntValue 设置为 2,然后将其绑定到在 UserControl IncrementIntView 中创建的依赖项属性。然后,当我按下 IncrementIntView 中的按钮时,它会将 IntValue 的值增加一。这在 UserControl IncrementIntView 中一切正常,但我不知道如何将更新后的 IntValue 发送回 MainWindowViewModel,它保持设置为 2。

IncrementIntView.xaml.cs

public partial class IncrementIntView : UserControl
{
    public int IntValue
    {
        get { return (int)GetValue(IntValueProperty); }
        set { SetValue(IntValueProperty, value); }
    }

    public static readonly DependencyProperty IntValueProperty =
        DependencyProperty.Register("IntValue", typeof(int), typeof(IncrementIntView),
                                        new PropertyMetadata(-1, new PropertyChangedCallback(IntValueChanged)));

    private static void IntValueChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        IncrementIntView detailGroup = dependencyObject as IncrementIntView;
        if (e.NewValue != null)
        {
            detailGroup.ViewModel.IntValue = (int)e.NewValue;
        }
    }

    public IncrementIntView()
    {
        InitializeComponent();
    }
}

IncrementIntViewModel.cs

public class IncrementIntViewModel : ViewModelBase
{
    private int intValue;
    public int IntValue
    {
        get { return intValue; }
        set { SetProperty(ref intValue, value); }
    }

    public IncrementIntViewModel()
    {
        incrementIntCommand = new Command(IncrementInt);
    }

    private Command incrementIntCommand;
    public Command IncrementIntCommand { get { return incrementIntCommand; } }
    public void IncrementInt()
    {
        IntValue++;
    }
}

IncrementIntView.xaml

<UserControl.DataContext>
    <local:IncrementIntViewModel x:Name="ViewModel" />
</UserControl.DataContext>

<Grid>
    <StackPanel Orientation="Horizontal">
        <Label Content="{Binding IntValue}" />
        <Button Content="Increment" Command="{Binding IncrementIntCommand}" Width="75" />
    </StackPanel>
</Grid>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

MainWindowViewModel.cs

public class MainWindowViewModel : ViewModelBase
{
    private int intValue = 2;
    public int IntValue
    {
        get { return intValue; }
        set { SetProperty(ref intValue, value); }
    }
}

MainWindow.xaml

<Window.DataContext>
    <local:MainWindowViewModel x:Name="ViewModel"/>
</Window.DataContext>

<Grid>
    <StackPanel Margin="10">
        <local:IncrementIntView IntValue="{Binding IntValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ElementName=ViewModel}" />
        <Label Content="{Binding IntValue}" />
    </StackPanel>
</Grid>

【问题讨论】:

  • 请注意,ElementName=ViewModel 是完全多余的。由于 MainWindowViewModel 实例已分配给 Window 的 DataContext,因此您无需显式指定 Binding 源。 Binding 应该只是IntValue="{Binding IntValue, Mode=TwoWay}",因为UpdateSourceTrigger=PropertyChanged 也是默认值。

标签: c# wpf mvvm data-binding dependency-properties


【解决方案1】:

我可以看到您的代码正在将 IntValue 从 MainWindowViewModel 的属性传递到 IncrementIntView 的依赖属性到 IncrementIntViewModel 的属性。

增量按钮正在更新 IncrementIntViewModel 的 IntValue 属性。不幸的是,IncrementIntViewModel 中发生的任何事情都不会反映回 IncrementIntView 的 IntValue 依赖属性。 TwoWay模式不在IncrementIntView的依赖属性和IncrementIntViewModel的属性之间,而是在MainWindowViewModel的属性和IncrementIntView的依赖属性之间。

简单的解决方案:将 MainWindow 的 Label 绑定到 IncrementIntViewModel 的 IntValue 属性,而不影响 View 的属性。

<local:IncrementIntView x:Name="iiv" IntValue="{Binding IntValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ElementName=ViewModel}" />
<Label Content="{Binding DataContext.IntValue, ElementName=iiv}" />
<!--You need to specify DataContext.IntValue, because you have same name for both view's dependency property and viewmodel's property-->

在这里您可以看到 MainWindowViewModel 的 IntValue 并不那么重要,因为它只是将值传递给 IncrementIntViewModel 一次,并且永远不会更新该值。

另一种解决方案:您需要触发值更改回 MainViewModel 的属性。

首先,MainViewModel 和 IncrementIntViewModel 之间没有联系。一种解决方案是将 MainViewModel 设置为单例,以便在 IncrementIntViewModel 内完成增量时,您也需要更新 MainViewModel 的属性。

在 MainViewModel.cs 中

public static MainWindowViewModel SingletonInstance { get; set; }
public MainWindowViewModel()
{
    if (SingletonInstance == null)
    {
        SingletonInstance = this;
    }
}

在 IncrementIntViewModel.cs 中

public void IncrementInt()
{
    IntValue++;
    MainWindowViewModel.SingletonInstance.IntValue = IntValue;
}

另一种解决方案:和上面的解决方案类似,但是我们不需要制作MainWindowViewModel的Singleton实例,因为MainWindow一开始就是单例的。

在 IncrementIntViewModel.cs 中

public void IncrementInt()
{
    IntValue++;
    ((MainWindowViewModel)App.Current.MainWindow.DataContext).IntValue = IntValue;
}

如果您打算将 IntValue 从 IncrementViewModel 的属性更新为 IncrementView 的依赖属性,那么您可能会问为什么需要这样做,因为 MVVM 应该在 V 和 VM 之间分离。 V 应该寻找 VM,而不是相反。

【讨论】:

  • 您的其他和其他解决方案都可以工作,但正如您所说,这对 MVVM 无效。我将退后一步,重新评估我的方法。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2013-03-15
  • 1970-01-01
  • 1970-01-01
  • 2011-03-28
  • 2014-05-23
  • 1970-01-01
相关资源
最近更新 更多