如何在MVVM框架中正确绑定用户控件的依赖属性 [英] How to correctly bind to a dependency property of a usercontrol in a MVVM framework
问题描述
我一直无法找到一个干净,简单的例子,说明如何正确地实现具有MVVM框架中的依赖属性的WPF的用户控件。我的代码下面失败,每当我分配usercontrol一个数据文本。
我试图:
- 从调用的ItemsControl设置依赖属性,
- 将该依赖属性的值设置为被调用的用户控件的ViewModel可用。
我还有很多学习,真诚感谢任何帮助。
这是最大的usercontrol中的ItemsControl那是通过依赖属性TextInControl(来自另一个问题的示例)调用InkStringView用户控件。
< ItemsControl
ItemsSource ={Binding Strings}x:Name =self>
< ItemsControl.ItemsPanel>
< ItemsPanelTemplate>
< StackPanel HorizontalAlignment =LeftVerticalAlignment =TopOrientation =Vertical/>
< / ItemsPanelTemplate>
< /ItemsControl.ItemsPanel>
< ItemsControl.ItemTemplate>
< DataTemplate>
< DataTemplate.Resources>
< Style TargetType =v:InkStringView>
< Setter Property =FontSizeValue =25/>
< Setter Property =HorizontalAlignmentValue =Left/>
< / Style>
< /DataTemplate.Resources>
< v:InkStringView TextInControl ={Binding text,ElementName = self}/>
< / DataTemplate>
< /ItemsControl.ItemTemplate>
< / ItemsControl>
这是具有依赖属性的InkStringView用户控件。
XAML:
< UserControl x:Class =Nova5.UI.Views.Ink.InkStringView
xmlns =http:// schemas。 $ microsoft.com/winfx/2006/xaml/presentation
xmlns:x =http://schemas.microsoft.com/winfx/2006/xaml
xmlns:mc =http://
xmlns:d =http://schemas.microsoft.com/expression/blend/2008
x:Name =mainInkStringView
mc:Ignorable =d
d:DesignHeight =300d:DesignWidth =300>
< Grid>
< Grid.RowDefinitions>
< RowDefinition />
< RowDefinition />
< /Grid.RowDefinitions>
< TextBlock Grid.Row =0Text ={Binding TextInControl,ElementName = mainInkStringView}/>
< TextBlock Grid.Row =1Text =我是第1行/>
< / Grid>
Code-Behind文件:
命名空间Nova5.UI.Views.Ink
{
public partial class InkStringView:UserControl
{
public InkStringView()
{
InitializeComponent();
this.DataContext = new InkStringViewModel(); < - 这个预防正确的绑定,什么
} - 请教
public String TextInControl
{
get {return(String)GetValue(TextInControlProperty); }
set {SetValue(TextInControlProperty,value); }
public static readonly DependencyProperty TextInControlProperty =
DependencyProperty.Register(TextInControl,typeof(String),typeof(InkStringView));
}
}
这是你不应该从 UserControl直接设置
DataContext
的许多原因之一
当您这样做时,您将无法再使用任何其他 DataContext
因为UserControl的 DataContext
被硬编码到只有 UserControl
可以访问的实例,哪种失败了WPF最大的优点是具有单独的UI和数据层。
WPF中有两种使用 UserControls
的主要方法
-
可以在任何地方使用独立的
UserControl
code> DataContext 被要求。
这种类型的
UserControl
通常会向任何人显示DependencyProperties
值要使用,如下所示:< v:InkStringView TextInControl ={Binding SomeValue}/> ;
我可以想到的典型例子是任何通用的例子,如Calendar控件或Popup控件。 p>
-
A
UserControl
,旨在与特定模型
或ViewModel
。
这些
UserControls
对我来说更为常见,可能是您正在寻找的案例。一个例子我将如何使用这样一个UserControl将是这样的:< v:InkStringView DataContext ={Binding MyInkStringViewModelProperty} />
或者更频繁地,它将与隐式
DataTemplate
。一个隐含的DataTemplate
是一个DataTemplate
,一个DataType
而没有Key
,WPF将自动使用此模板,只要它要呈现指定类型的对象。< Window.Resources>
< DataTemplate DataType ={x:Type m:InkStringViewModel}>
< v:InkStringView />
< / DataTemplate>
< Window.Resources>
<! - 绑定到单个ViewModel - >
< ContentPresenter Content ={Binding MyInkStringViewModelProperty}/>
<! - 绑定到ViewModels的集合 - >
< ItemsControl ItemsSource ={Binding MyCollectionOfInkStringViewModels}/>
否
ContentPresenter.ItemTemplate
或ItemsControl.ItemTemplate
在使用此方法时需要。
不要将这两种方法组合起来,并不顺利:)
但无论如何,要解释您的具体问题更多细节
当您创建这样的UserControl
< v:InkStringView TextInControl ={Binding text}/>
你基本上是说
var vw = new InkStringView()
vw.TextInControl = vw.DataContext.text;
vw.DataContext
XAML,所以它从父项继承,这导致
vw.DataContext = Strings [x];
所以你的绑定设置 TextInControl = vw.DataContext.text
是有效的,并在运行时解析很好。
但是,当您在UserControl构造函数中运行时,
this.DataContext = new InkStringViewModel();
DataContext
设置为一个值,所以不再自动从父进程继承。
所以现在运行的代码看起来像这样:
var vw = new InkStringView()
vw.DataContext = new InkStringViewModel();
vw.TextInControl = vw.DataContext.text;
自然而然, InkStringViewModel
没有属性称为 text
,因此绑定在运行时失败。
I have been unable to find a clean, simple, example of how to correctly implement a usercontrol with WPF that has a dependency property within the MVVM framework. My code below fails whenever I assign the usercontrol a datacontext.
I am trying to:
- Set the dependency property from the calling ItemsControl , and
- Make the value of that dependency property available to the ViewModel of the called usercontrol.
I still have a lot to learn and sincerely appreciate any help.
This is the ItemsControl in the topmost usercontrol that is making the call to the InkStringView usercontrol with the dependency property TextInControl (example from another question).
<ItemsControl
ItemsSource="{Binding Strings}" x:Name="self" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="v:InkStringView">
<Setter Property="FontSize" Value="25"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
</DataTemplate.Resources>
<v:InkStringView TextInControl="{Binding text, ElementName=self}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the InkStringView usercontrol with the dependency property.
XAML:
<UserControl x:Class="Nova5.UI.Views.Ink.InkStringView"
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"
x:Name="mainInkStringView"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding TextInControl, ElementName=mainInkStringView}" />
<TextBlock Grid.Row="1" Text="I am row 1" />
</Grid>
Code-Behind file:
namespace Nova5.UI.Views.Ink
{
public partial class InkStringView : UserControl
{
public InkStringView()
{
InitializeComponent();
this.DataContext = new InkStringViewModel(); <--THIS PREVENTS CORRECT BINDING, WHAT
} --ELSE TO DO?????
public String TextInControl
{
get { return (String)GetValue(TextInControlProperty); }
set { SetValue(TextInControlProperty, value); }
}
public static readonly DependencyProperty TextInControlProperty =
DependencyProperty.Register("TextInControl", typeof(String), typeof(InkStringView));
}
}
That is one of the many reasons you should never set the DataContext
directly from the UserControl
itself.
When you do so, you can no longer use any other DataContext
with it because the UserControl's DataContext
is hardcoded to an instance that only the UserControl
has access to, which kind of defeats one of WPF's biggest advantages of having separate UI and data layers.
There are two main ways of using UserControls
in WPF
A standalone
UserControl
that can be used anywhere without a specificDataContext
being required.This type of
UserControl
normally exposesDependencyProperties
for any values it needs, and would be used like this:<v:InkStringView TextInControl="{Binding SomeValue}" />
Typical examples I can think of would be anything generic such as a Calendar control or Popup control.
A
UserControl
that is meant to be used with a specificModel
orViewModel
only.These
UserControls
are far more common for me, and is probably what you are looking for in your case. An example of how I would use such a UserControl would be this:<v:InkStringView DataContext="{Binding MyInkStringViewModelProperty}" />
Or more frequently, it would be used with an implicit
DataTemplate
. An implicitDataTemplate
is aDataTemplate
with aDataType
and noKey
, and WPF will automatically use this template anytime it wants to render an object of the specified type.<Window.Resources> <DataTemplate DataType="{x:Type m:InkStringViewModel}"> <v:InkStringView /> </DataTemplate> <Window.Resources> <!-- Binding to a single ViewModel --> <ContentPresenter Content="{Binding MyInkStringViewModelProperty}" /> <!-- Binding to a collection of ViewModels --> <ItemsControl ItemsSource="{Binding MyCollectionOfInkStringViewModels}" />
No
ContentPresenter.ItemTemplate
orItemsControl.ItemTemplate
is needed when using this method.
Don't mix these two methods up, it doesn't go well :)
But anyways, to explain your specific problem in a bit more detail
When you create your UserControl like this
<v:InkStringView TextInControl="{Binding text}" />
you are basically saying
var vw = new InkStringView()
vw.TextInControl = vw.DataContext.text;
vw.DataContext
is not specified anywhere in the XAML, so it gets inherited from the parent item, which results in
vw.DataContext = Strings[x];
so your binding that sets TextInControl = vw.DataContext.text
is valid and resolves just fine at runtime.
However when you run this in your UserControl constructor
this.DataContext = new InkStringViewModel();
the DataContext
is set to a value, so no longer gets automatically inherited from the parent.
So now the code that gets run looks like this:
var vw = new InkStringView()
vw.DataContext = new InkStringViewModel();
vw.TextInControl = vw.DataContext.text;
and naturally, InkStringViewModel
does not have a property called text
, so the binding fails at runtime.
这篇关于如何在MVVM框架中正确绑定用户控件的依赖属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!