脱发和MVVM用户控件

4
我有一个使用MVVM模式编写的C#和WPF的用户控件。
我想做的就是将绑定的ViewModel中的属性暴露给控件外部。我希望能够将其绑定,希望任何对该属性做出的更改都能被绑定到该值的任何控件外部所捕获。
这听起来很简单,但它让我抓狂了(我的头发已经不多了)。
我在用户控件中有一个依赖属性。ViewModel拥有实现INotifyPropertyChanged接口的属性,并正确调用PropertyChanged事件。
一些问题:
1)如何在不破坏MVVM分离的情况下捕获对ViewModel属性的更改并将其与DependencyProperty关联起来?到目前为止,我唯一成功的方法是在控件的代码后台中分配ViewModel的PropertyChanged事件,这绝对不是MVVM。
2)使用上述方法,我可以让Dependency属性启动其PropertyChangedCallback,但在控件外部绑定到它的任何内容都无法捕获更改。
必须有一种简单的方法来完成所有这些。请注意,我没有在此处发布任何代码 - 我希望不要用我的现有代码影响答案。此外,你们可能会嘲笑它...
Rob
好的,为了澄清,以下是代码示例:
用户控件的代码后台:
   public static DependencyProperty NewRepositoryRunProperty = DependencyProperty.Register("NewRepositoryRun", typeof(int?), typeof(GroupTree),
                                                                new FrameworkPropertyMetadata( null, new PropertyChangedCallback(OnNewRepositoryRunChanged)));
    public int? NewRepositoryRun
    {
        get { return (int?)GetValue(NewRepositoryRunProperty); }
        set
        {
            SetValue(NewRepositoryRunProperty, value);
        }
    }

    private static void OnNewRepositoryRunChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (e.OldValue != e.NewValue)
        {

        }
    }

    public GroupTree()
    {
        InitializeComponent();

        GroupTreeVM vm = new GroupTreeVM();

        this.DataContext = vm;

    }

视图模型(GroupTreeVM.cs)


   private int? _NewRepositoryRun;
    public int? NewRepositoryRun
    {
        get
        {
            return _NewRepositoryRun;
        }
        set
        {
            _NewRepositoryRun = value; 
            NotifyPropertyChanged();
        }
    }

6
掉发的问题可能属于其他网站,请问我们有美容网站吗?也许你的CRT显示器正在危害你的健康,请切换到纸质。或者,您可以从您的帖子中删除与编程无关的文本... - Alexei Levenkov
1
我不知道你在说什么。WPF不支持头发或缺乏头发,我在你的帖子中也没有看到任何XAML或C#代码。投票关闭。-1 - Federico Berasategui
1
@Corcus 哦,真是的,那个问题。这里有一个提示:(this.Content as FrameworkElement).DataContext = this 是你能做的最愚蠢的事情之一。只需使用 Binding.ElementName 引用在根上定义的属性即可。 - user1228
请注意,您并没有明确说明您已经完成了它,但我立即知道发生了什么,因为当人们尝试为其用户控件创建视图模型时,他们都会遇到这些问题。我不是魔法师,只是曾经有过类似的经历。不,撤销那个,我是魔法师。听我的话,否则我会让你的小弟弟消失。 - user1228
1
好的,继续用锤子打自己的头。毕竟那是你的头。另外,噼里啪啦,没有了小弟弟。享受坐着撒尿的感觉吧。 - user1228
显示剩余7条评论
2个回答

12

现在是我每周的“不要那样做”的答案...

为您的UserControl创建ViewModel是一种代码异味。

您之所以遇到这个问题,是因为存在这种异味,这应该表明您正在做错事情。

解决方案是放弃为UserControl构建的VM。如果它包含业务逻辑,则应将其移动到另一个ViewModel中的适当位置。

您应该将UserControl视为更复杂的控件。TextBox有自己的ViewModel吗?没有。您将VM的属性绑定到控件的Text属性上,控件会在其UI中显示您的文本。

在MVVM中,将UserControl视为以下内容-对于每个模型,您都有一个UserControl,并且它旨在向用户呈现该模型中的数据。您可以在任何地方使用它来向用户显示该模型。需要按钮吗?在您的UserControl上公开ICommand属性,让您的业务逻辑绑定到它。您的业务逻辑是否需要了解内部发生的事情?添加路由事件。

通常,在WPF中,如果您发现自己问为什么做某事会感到疼痛,那是因为您不应该这样做。


1
很棒的答案。我自己也花了一些时间才意识到这一点。但需要注意的是:当使用UserControl作为“子视图”(甚至是它自己的视图)时,它自己的VM是有意义的。 - BradleyDotNET
1
用户控件的一些规则:如果其中有依赖属性,就不应该将DataContext设置为自身,也不应该为UserControl设置“自己的”视图模型。然后,在您的用户控件中使用数据绑定到依赖属性时,应始终使用ElementName或RelativeSource。 - blindmeis
@O.O 不是的。每个视图都应该扩展一个基本视图类,并具有适当的属性。如果视图模型要求用户已登录,则如果记录用户登录的代码对所有VM都可用,那将非常有帮助。为什么要把它放在控件中?那很傻。不要这样做。 - user1228
@Will 我不会把那个逻辑放在用户控件中,我会把它放在用户控件的视图模型中,因为视图模型的职责是为视图保存状态。你不是在讨论一个全局视图模型或类似的东西吗? - O.O
虚拟机的工作是解释用户输入并改变应用程序的状态。视图负责自己的状态。有很多不同的正确方法来做到这一点。如果您仍然对此有疑问,那么提出一个实际问题可能会更值得。 - user1228
显示剩余2条评论

0
也许我误解了,但是看起来你试图在代码后台使用绑定?
public MyUserControl()
{
    InitializeComponent();

    // Set your datacontext.

    var binding = new Binding("SomeVMProperty");
    binding.Source = this.DataContext;

    SetBinding(MyDependencyProperty, binding);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接