带有 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
WPF User Control hell with MVVM and Dependency Properties
提问by Jon Mitchell
This is what I'm trying to do:
这就是我想要做的:
- I'm writing a
UserControl
that 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:
所以我有两个问题:
Am I right in thinking that if a UserControl is being consumed in XAML then the UserControl must set the ViewModel as its
DataContext
when the control'sLoaded
event fires instead of using the<DataTemplate>
method?How do I allow users to data bind to my control's dependency properties while still being data bound to my ViewModel?
我是否正确地认为,如果在 XAML 中使用 UserControl,那么
DataContext
当控件的Loaded
事件触发而不是使用该<DataTemplate>
方法时,UserControl 必须将 ViewModel 设置为它的?如何允许用户将数据绑定到我的控件的依赖属性,同时仍然将数据绑定到我的 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:
您应该将两个用例分开:
- The (user) control that will be consumed by other developers.
- The user control that will be consumed by your application.
- 其他开发人员将使用的(用户)控件。
- 您的应用程序将使用的用户控件。
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 UserControl
is part of the "View" in "MVVM" just like the TextBox
or ListView
controls are part of the View.
AUserControl
是“MVVM”中“视图”的一部分,就像TextBox
或ListView
控件是视图的一部分一样。
Whether you decide to use MVVM to develop your UserControl
itself or write it in QBASIC (not recommended) it does not break the MVVM pattern for the consumers of your UserControl
so long as they can do every thing they need with your UserControl
by binding to DependencyProperty
's exposed on your UserControl
. i.e. Your UserControl
should 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 UserControl
is in a different assembly or not I cannot see how that makes a difference.
如果您UserControl
在不同的程序集中,我看不出这有什么不同。
That said many would advocate you build your UserControl
using 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 UserControl
handling 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!