带有 MVVM 和依赖属性的 WPF 用户控制地狱

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2637662/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 21:40:51  来源:igfitidea点击:

WPF User Control hell with MVVM and Dependency Properties

wpfuser-controlsmvvmdependency-properties

提问by Jon Mitchell

This is what I'm trying to do:

这就是我想要做的:

  • I'm writing a UserControlthat I want to be consumed by other developers.
  • I want end users to be able to use my control using Dependency Properties.

    <lib:ControlView ControlsText={Binding Path=UsersOwnViewModelText} />
    
  • I'm using the MVVM pattern.

  • I'm binding my ViewModels to their View's using <DataTemplates>

    <DataTemplate DataType="{x:Type local:ControlViewModel}">  
        <local:ControlView />  
    </DataTemplate>
    
  • 我正在写一个UserControl我想被其他开发人员使用的。
  • 我希望最终用户能够使用依赖属性来使用我的控件。

    <lib:ControlView ControlsText={Binding Path=UsersOwnViewModelText} />
    
  • 我正在使用 MVVM 模式。

  • 我正在将我的 ViewModels 绑定到他们的 View 使用 <DataTemplates>

    <DataTemplate DataType="{x:Type local:ControlViewModel}">  
        <local:ControlView />  
    </DataTemplate>
    

So I have two questions:

所以我有两个问题:

  1. Am I right in thinking that if a UserControl is being consumed in XAML then the UserControl must set the ViewModel as its DataContextwhen the control's Loadedevent fires instead of using the <DataTemplate>method?

  2. How do I allow users to data bind to my control's dependency properties while still being data bound to my ViewModel?

  1. 我是否正确地认为,如果在 XAML 中使用 UserControl,那么DataContext当控件的Loaded事件触发而不是使用该<DataTemplate>方法时,UserControl 必须将 ViewModel 设置为它的?

  2. 如何允许用户将数据绑定到我的控件的依赖属性,同时仍然将数据绑定到我的 ViewModel?

采纳答案by Bryce Kahle

First off, I don't think MVVM is a good choice if you are developing a UserControl that will be consumed by others. A lookless control is what you really should be developing. Jeremiah Morrill has a blog postabout this subject.

首先,如果您正在开发一个将由其他人使用的 UserControl,我认为 MVVM 不是一个好的选择。无外观控件才是您真正应该开发的控件。Jeremiah Morrill 有一篇关于这个主题的博客文章

With that said, you can set the datacontext with XAML if you have a default public constructor.

话虽如此,如果您有默认的公共构造函数,则可以使用 XAML 设置数据上下文。

Inside ControlView.xaml put:

在 ControlView.xaml 里面放:

<UserControl.DataContext>
    <local:ControlViewModel />
</UserControl.DataContext>

回答by Kent Boogaart

You should separate the two use cases:

您应该将两个用例分开:

  1. The (user) control that will be consumed by other developers.
  2. The user control that will be consumed by your application.
  1. 其他开发人员将使用的(用户)控件。
  2. 您的应用程序将使用的用户控件。

Importantly, the latter depends on the former - not vice versa.

重要的是,后者取决于前者——反之亦然。

Use case 1 would use dependency properties, template bindings, all the things that go into making a regular WPF control:

用例 1 将使用依赖属性、模板绑定以及制作常规 WPF 控件的所有内容:

MyControl.cs:

我的控制.cs:

public class MyControl : Control
{
    // dependency properties and other logic
}

Generic.xaml:

通用.xaml:

<ControlTemplate Type="local:MyControl">
    <!-- define the default look in here, using template bindings to bind to your d-props -->
</ControlTemplate>

You would then define use case 2 as:

然后,您将用例 2 定义为:

MyViewModel.cs:

MyViewModel.cs:

public class MyViewModel : ViewModel
{
    // properties and business logic
}

MyView.xaml:

我的视图.xaml:

<UserControl ...>
    <local:MyControl SomeProperty="{Binding SomePropertyOnViewModel}" .../>
</UserControl>

Best of both worlds with a clean separation. Other developers depend only on the control, which could (and probably should) be in a completely different assembly than your view model and view.

两全其美,干净分离。其他开发人员仅依赖于控件,它可以(并且可能应该)与您的视图模型和视图位于完全不同的程序集中。

回答by Mura

Basically, instead of binding your UserControl's datacontext to the userControlViewModel, it's better to do it on the first child element of the user control. That way, all the references that you make within the control will be bound to the userControlViewModel, but the dependencies properties can be set from the data context set where you want to use your UserControl.

基本上,与其将 UserControl 的数据上下文绑定到 userControlViewModel,不如在用户控件的第一个子元素上执行此操作。这样,您在控件中创建的所有引用都将绑定到 userControlViewModel,但可以从要使用 UserControl 的数据上下文集设置依赖项属性。

This is from a project I'm working at:

这是来自我正在工作的一个项目:

<UserControl x:Class="Six_Barca_Main_Interface.MyUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:Six_Barca_Main_Interface"
             xmlns:System="clr-namespace:System;assembly=mscorlib" 
             mc:Ignorable="d" 
             d:DesignHeight="900" d:DesignWidth="900">

    <DockPanel  x:Name="rootDock" >
        <TextBlock>{Binding SomethingInMyUserControlViewModel}</TabControl>
    </DockPanel>
</UserControl>

Then on the code behind:

然后在后面的代码上:

public partial class MyUserControl : UserControl
{
    UserControlViewModel _vm;

    public MyUserControl()
    {
        InitializeComponent();

        //internal viewModel set to the first child of MyUserControl
         rootDock.DataContext = new UserControlViewModel();

        _vm = (UserControlViewModel)rootDock.DataContext;


        //sets control to be able to use the viewmodel elements

     }

     #region Dependency properties 
     public string textSetFromApplication
     {
         get{return (string)GetValue(textSetFromApplicationProperty);}
         set{SetValue(textSetFromApplicationProperty, value);}
     }

     public static readonly DependencyProperty textSetFromApplicationProperty = DependencyProperty.Register("textSetFromApplication", typeof(string), typeof(MyUserControl), new PropertyMetadata(null, OnDependencyPropertyChanged));

     private static void  OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
     {
        ((MyUserControl)d)._vm.SomethingInMyUserControlViewModel = 
             e.NewValue as string;
     }
#endregion

Then when you use this on your main view, you can set the dependency property with the value you want to pass to MyUSerControl

然后当您在主视图上使用它时,您可以使用要传递给 MyUSerControl 的值设置依赖属性

回答by markmnl

A UserControlis part of the "View" in "MVVM" just like the TextBoxor ListViewcontrols are part of the View.

AUserControl是“MVVM”中“视图”的一部分,就像TextBoxListView控件是视图的一部分一样。

Whether you decide to use MVVM to develop your UserControlitself or write it in QBASIC (not recommended) it does not break the MVVM pattern for the consumers of your UserControlso long as they can do every thing they need with your UserControlby binding to DependencyProperty's exposed on your UserControl. i.e. Your UserControlshould expose the properties it is dependentupon (hence the name). Once you grasp this DependencyProperty's suddenly make a whole lot of sense and you want their helpful on changed event handlers and default values you specify in their constructor.

无论你决定使用 MVVM 来开发你UserControl自己还是用 QBASIC 编写它(不推荐)它都不会破坏你的消费者的 MVVM 模式,UserControl只要他们可以UserControl通过绑定到DependencyProperty的暴露来做他们需要的一切事情你的UserControl。即您UserControl应该公开它所依赖的属性(因此得名)。一旦你掌握了这一点DependencyProperty,就会突然变得很有意义,并且你希望它们对更改的事件处理程序和您在构造函数中指定的默认值有所帮助。

If your UserControlis in a different assembly or not I cannot see how that makes a difference.

如果您UserControl在不同的程序集中,我看不出这有什么不同。

That said many would advocate you build your UserControlusing the MVVM pattern itself for all the good reasons MVVM brings e.g. helping another developer looking at your code. However some things simply are not possible and/or much harder more complex and less performant hacking the XAML to do this - I am not talking about your garden variety Add User Form but for example a UserControlhandling the layout of thousands of visuals. Furthermore since you are working in your View you do NOTwant your UserControl's ViewModels mixed in with you applications!

这就是说,许多人会提倡您UserControl使用 MVVM 模式本身来构建您的构建,因为 MVVM 带来了所有很好的理由,例如帮助另一个开发人员查看您的代码。然而,有些事情根本不可能和/或更难更复杂和性能更差的 XAML 黑客来做到这一点 - 我不是在谈论你的花园品种添加用户表单,而是例如UserControl处理数千个视觉效果的布局。此外,由于您在 View 中工作,您希望您UserControl的 ViewModel 与您的应用程序混合!

Basically I am saying it is well within MVVM not to use MVVM on your View!

基本上我是说在 MVVM 中不要在您的视图上使用 MVVM!