wpf 绑定 UserControl 依赖属性和 MVVM
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22247633/
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
Binding UserControl Dependency Property and MVVM
提问by joerg
I have a MainWindow containing a UserControl, both implemented in MVVM-pattern. The MainWindowVM has properties that I want to bind to properties in the UserControl1VM. But this doesn't work.
我有一个包含 UserControl 的 MainWindow,两者都以 MVVM 模式实现。MainWindowVM 具有我想绑定到 UserControl1VM 中的属性的属性。但这不起作用。
Here's some code (the viewmodels use some kind of mvvm-framework that implement the INotifyPropertyChanged in a ViewModelBase-class but that's hopefully no problem):
这是一些代码(视图模型使用某种 mvvm 框架在 ViewModelBase 类中实现 INotifyPropertyChanged 但希望没有问题):
MainWindow.xaml:
主窗口.xaml:
<Window x:Class="DPandMVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DPandMVVM"
Title="MainWindow" Height="300" Width="300">
<Grid>
<local:UserControl1 TextInControl="{Binding Text}" />
</Grid>
</Window>
CodeBehind MainWindow.xaml.cs:
代码隐藏 MainWindow.xaml.cs:
using System.Windows;
namespace DPandMVVM
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowVM();
}
}
}
MainWindow-ViewModel MainWindowVM.cs:
MainWindow-ViewModel MainWindowVM.cs:
namespace DPandMVVM
{
public class MainWindowVM : ViewModelBase
{
private string _text;
public string Text { get { return _text; } }
public MainWindowVM()
{
_text = "Text from MainWindowVM";
}
}
}
And here the UserControl1.xaml:
这里是 UserControl1.xaml:
<UserControl x:Class="DPandMVVM.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding TextInTextBlock}" />
</Grid>
</UserControl>
The Codebehind UserControl1.xaml.cs:
代码隐藏 UserControl1.xaml.cs:
using System.Windows.Controls;
namespace DPandMVVM
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
DataContext = new UserControl1VM();
}
}
}
And the Viewmodel UserControl1VM.cs:
和 Viewmodel UserControl1VM.cs:
using System.Windows;
namespace DPandMVVM
{
public class UserControl1VM : DependencyObject
{
public UserControl1VM()
{
TextInControl = "TextfromUserControl1VM";
}
public string TextInControl
{
get { return (string)GetValue(TextInControlProperty); }
set { SetValue(TextInControlProperty, value); }
}
public static readonly DependencyProperty TextInControlProperty =
DependencyProperty.Register("TextInControl", typeof(string), typeof(UserControl1VM));
}
}
With this constellation the DP cannot be found in MainWindow.xaml.
使用此星座无法在 MainWindow.xaml 中找到 DP。
What am I doing wrong?
我究竟做错了什么?
回答by Rohit Vats
Firstof all you want DependencyProperty TextInControl
to be declared inside UserControl1
if you want to bind it from outside.
首先,如果您想从外部绑定它,您希望在TextInControl
内部声明DependencyProperty UserControl1
。
Move the declaration of DP inside of UserControl1
.
将 DP 的声明移到UserControl1
.
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public string TextInControl
{
get { return (string)GetValue(TextInControlProperty); }
set { SetValue(TextInControlProperty, value); }
}
public static readonly DependencyProperty TextInControlProperty =
DependencyProperty.Register("TextInControl", typeof(string),
typeof(UserControl1));
}
Secondyou have externally set DataContext of UserControl to UserControl1VM
,
其次,您在外部将 UserControl 的 DataContext 设置为UserControl1VM
,
public UserControl1()
{
InitializeComponent();
DataContext = new UserControl1VM(); <-- HERE (Remove this)
}
So WPF binding engine looking for property Text
in UserControl1VM
instead of MainWindowVM
. Remove setting DataContext and update XAML of UserControl1 to this:
所以 WPF 绑定引擎Text
在UserControl1VM
而不是MainWindowVM
. 删除设置 DataContext 并将 UserControl1 的 XAML 更新为:
<UserControl x:Class="DPandMVVM.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="userControl1">
<Grid>
<TextBlock Text="{Binding TextInTextBlock, ElementName=userControl1}" />
</Grid>
</UserControl>
Bind DP using ElementName
by setting x:Name
on UserControl.
ElementName
通过x:Name
UserControl 上的设置绑定 DP 。
UPDATE
更新
In case you want to have ViewModel
intact for UserControl
, you have to update binding in MainWindow. Explicitly tell WPF binding engine to look for property in MainWindow's DataContext using ElementName
in binding like this:
如果您想要ViewModel
完整的 for UserControl
,您必须更新 MainWindow 中的绑定。显式地告诉 WPF 绑定引擎在 MainWindow 的 DataContext 中使用ElementName
绑定查找属性,如下所示:
<local:UserControl1 TextInControl="{Binding DataContext.Text,
ElementName=mainWindow}" />
For this you need to set x:Name="mainWindow"
on window root level.
为此,您需要x:Name="mainWindow"
在窗口根级别进行设置。
回答by gomi42
The XAML of your control right now reference the property TextInTextBlock
via the DataContext which in turn "Points" to your main window's view model. Reference the data of the control and you are done (btw do not set the DataContext for that reason - the binding won't work any more):
您的控件的 XAML 现在TextInTextBlock
通过 DataContext引用该属性,而 DataContext 又“指向”您的主窗口的视图模型。引用控件的数据就完成了(顺便说一句,不要为此设置 DataContext - 绑定将不再起作用):
<UserControl x:Class="DPandMVVM.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="self">
<Grid>
<TextBlock Text="{Binding TextInTextBlock, ElementName=self}" />
</Grid>
</UserControl>
回答by phillk6751
I have a method that I believe is a lot simpler, and probably more true to MVVM.
我有一种方法,我认为它更简单,而且可能更适合 MVVM。
In the main window XAML:
在主窗口 XAML 中:
<myNameSpace:myUserControl DataContext="{Binding Status}"/>
In your main view model (the data context of the main window:
在您的主视图模型中(主窗口的数据上下文:
public myUserControlViewModel Status { set; get; }
now you can in the constructor (or whenever you want to instantiate it):
现在你可以在构造函数中(或者任何时候你想实例化它):
Status = new myUserControlViewModel();
then if you want to set the text property:
那么如果你想设置 text 属性:
Status.Text = "foo";
and make sure you have the binding setup to a property named Text inside your myUserControlViewModel class:
并确保在 myUserControlViewModel 类中设置了一个名为 Text 的属性的绑定:
<TextBox Text="{Binding Text}"/>
and make sure the property fires PropertyChanged, of-course.
并确保该属性触发 PropertyChanged,当然。
Plus, if you use Resharper. You can create a Design instance of the UserControl in your XAML so that it can link the bindings and not tell you that the property is never used by doing this:
另外,如果您使用 Resharper。您可以在 XAML 中创建 UserControl 的 Design 实例,以便它可以链接绑定,而不会通过执行以下操作告诉您该属性从未使用过:
<UserControl x:Class="myNameSpace.myUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:myNameSpace="clr-namespace:myNameSpace"
d:DataContext="{d:DesignInstance myNameSpace.myUserControl}"
mc:Ignorable="d" ...>
This part:
这部分:
xmlns:myNameSpace="clr-namespace:myNameSpace"
d:DataContext="{d:DesignInstance myNameSpace.myUserControl}"
回答by Neil Alderson
This is how I do UserControls with MVVM and DP binding. It's similar to Rohit's answer but with some slight changes. Basically you need to set the Control's internal view model to be the DataContext of the root container within the UserControl rather than the UserControl itself, that way it will not interfere with DP bindings.
这就是我如何使用 MVVM 和 DP 绑定来执行 UserControls。它类似于 Rohit 的答案,但有一些细微的变化。基本上,您需要将 Control 的内部视图模型设置为 UserControl 内根容器的 DataContext 而不是 UserControl 本身,这样它就不会干扰 DP 绑定。
E.g.
例如
UserControl XAML
用户控件 XAML
<UserControl x:Class="DPandMVVM.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="userControl1">
<Grid x:Name="Root">
<TextBlock Text="{Binding TextFromVM}" />
</Grid>
UserControl Code-behind
用户控件代码隐藏
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.ViewModel = new UserControlVM();
}
public UserControlVM ViewModel
{
get { return this.Root.DataContext as UserControlVM ; }
set { this.Root.DataContext = value; }
}
public string TextFromBinding
{
get { return (string)GetValue(TextFromBindingProperty); }
set { SetValue(TextFromBindingProperty, value); }
}
public static readonly DependencyProperty TextFromBindingProperty =
DependencyProperty.Register("TextFromBinding", typeof(string), typeof(UserControl1), new FrameworkPropertyMetadata(null, OnTextBindingChanged));
private static void OnTextBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uc = d as UserControl1;
uc.ViewModel.TextFromVM = e.NewValue as string;
}
}
This means that the control derives it's values from the Root element DataContext which is our ViewModel but the ViewModel can be updated via a DP binding from outside the control (in your case a binding to the parent Window's ViewModel, see below)
这意味着控件从根元素 DataContext 派生它的值,它是我们的 ViewModel 但 ViewModel 可以通过控件外部的 DP 绑定更新(在您的情况下绑定到父窗口的 ViewModel,见下文)
Window XAML
窗口 XAML
<Window x:Class="DPandMVVM.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DPandMVVM"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="window1">
<Grid x:Name="Root">
<local:userControl1 TextFromBinding="{Binding TextFromWindowVM}" />
</Grid>
回答by Prethen
Here's a possible working solution for you. However, I've noted in a comment above that this will work in code and perhaps (like my situation) will show up as an error (Object Not Found) in the designer:
这是适合您的可行解决方案。但是,我在上面的评论中指出,这将在代码中起作用,并且可能(如我的情况)在设计器中显示为错误(未找到对象):
<local:UserControl1 TextInControl="{Binding DataContext.Text,
Source={x:Reference <<Your control that contains the DataContext here>>}}" />
I'd rather to have a cleaner solution, though, without any designer errors. I wish to find out how to properly bind a dependency property in a user control to a value coming from the window it's contained in. What I'm finding is that whatever I try to do (short of what I showed above), such as using ElementName and/or AncestorType/Level, etc., the debugger complains that it can't find the source and shows that it's looking for the source inside the context of the user control! It's like I can't break out of the user control context when doing Binding logic in the use of that control (other than that "designer-breaking" solution above).
不过,我宁愿有一个更干净的解决方案,没有任何设计者错误。我希望找出如何将用户控件中的依赖属性正确绑定到来自它所包含的窗口的值。我发现无论我尝试做什么(没有我上面显示的内容),例如使用 ElementName 和/或 AncestorType/Level 等,调试器抱怨它找不到源并显示它正在用户控件的上下文中寻找源!就像在使用该控件(除了上面的“破坏设计者”的解决方案之外)执行绑定逻辑时,我无法脱离用户控件上下文。
UPDATE: I noticed that this might not work for you as your situation might force a problem I just noticed if I change my own source to reference the window instead of a control that has the data context. If I reference the window then I end up with a cyclical redundancy. Perhaps you'll figure out a way to use the Source version of the binding that will work okay for you.
更新:我注意到这可能对您不起作用,因为您的情况可能会导致出现问题,我刚刚注意到,如果我更改自己的源以引用窗口而不是具有数据上下文的控件。如果我引用窗口,那么我最终会得到一个循环冗余。也许您会想出一种方法来使用对您来说可以正常工作的绑定的源版本。
I must also add that my situation is probably a bit more complex since my usercontrol is used in the context of a popup.
我还必须补充一点,我的情况可能有点复杂,因为我的用户控件是在弹出窗口的上下文中使用的。