如何在 MVVM 框架中正确绑定到用户控件的依赖属性 [英] How to correctly bind to a dependency property of a usercontrol in a MVVM framework
问题描述
我一直无法找到一个干净、简单的示例,说明如何正确在 MVVM 框架内使用具有 DependencyProperty
的 WPF 实现用户控件.每当我为用户控件分配一个 DataContext
时,我下面的代码就会失败.
I have been unable to find a clean, simple, example of how to correctly implement a usercontrol with WPF that has a DependencyProperty
within the MVVM framework. My code below fails whenever I assign the usercontrol a DataContext
.
我正在尝试:
- 从调用 ItemsControl 设置
DependencyProperty
,和 - 使该
DependencyProperty
的值可用于被调用用户控件的 ViewModel.
- Set the
DependencyProperty
from the calling ItemsControl , and - Make the value of that
DependencyProperty
available to the ViewModel of the called usercontrol.
我还有很多东西要学,衷心感谢您的帮助.
I still have a lot to learn and sincerely appreciate any help.
这是最顶层用户控件中的 ItemsControl
,它使用 DependencyProperty
TextInControl
调用 InkStringView
用户控件代码>(来自另一个问题的示例).
This is the ItemsControl
in the topmost usercontrol that is making the call to the InkStringView
usercontrol with the DependencyProperty
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>
这是带有 DependencyProperty
的 InkStringView
用户控件.
Here is the InkStringView
usercontrol with the DependencyProperty
.
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>
</UserControl>
代码隐藏文件:
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));
}
}
推荐答案
这是永远不要直接从 UserControl
本身设置 DataContext
的众多原因之一.
That is one of the many reasons you should never set the DataContext
directly from the UserControl
itself.
当你这样做时,你不能再使用任何其他 DataContext
,因为 UserControl 的 DataContext
被硬编码到一个只有 UserControl
的实例代码> 可以访问,这违背了 WPF 具有独立 UI 和数据层的最大优势之一.
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.
WPF中UserControls
主要有两种使用方式
There are two main ways of using UserControls
in WPF
一个独立的
UserControl
,可以在任何地方使用而无需特定的DataContext
.
A standalone
UserControl
that can be used anywhere without a specificDataContext
being required.
这种类型的 UserControl
通常为它需要的任何值公开 DependencyProperties
,并且会像这样使用:
This type of UserControl
normally exposes DependencyProperties
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.
一个 UserControl
,仅用于特定的 Model
或 ViewModel
.
A UserControl
that is meant to be used with a specific Model
or ViewModel
only.
这些 UserControls
对我来说更常见,并且可能是您正在寻找的.我将如何使用这样的 UserControl 的一个例子是:
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}" />
或者更频繁地,它将与隐式 DataTemplate
一起使用.隐式 DataTemplate
是带有 DataType
且没有 Key
的 DataTemplate
,WPF 将在任何时候自动使用此模板想要呈现指定类型的对象.
Or more frequently, it would be used with an implicit DataTemplate
. An implicit DataTemplate
is a DataTemplate
with a DataType
and no Key
, 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}" />
使用此方法时不需要 ContentPresenter.ItemTemplate
或 ItemsControl.ItemTemplate
.
No ContentPresenter.ItemTemplate
or ItemsControl.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
当您像这样创建 UserControl 时
When you create your UserControl like this
<v:InkStringView TextInControl="{Binding text}" />
你基本上是在说
var vw = new InkStringView()
vw.TextInControl = vw.DataContext.text;
vw.DataContext
未在 XAML 中的任何位置指定,因此它是从父项继承的,这导致
vw.DataContext
is not specified anywhere in the XAML, so it gets inherited from the parent item, which results in
vw.DataContext = Strings[x];
因此您设置 TextInControl = vw.DataContext.text
的绑定是有效的,并且在运行时解析得很好.
so your binding that sets TextInControl = vw.DataContext.text
is valid and resolves just fine at runtime.
但是,当您在 UserControl 构造函数中运行它时
However when you run this in your UserControl constructor
this.DataContext = new InkStringViewModel();
DataContext
被设置为一个值,因此不再自动从父级继承.
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;
当然,InkStringViewModel
没有名为 text
的属性,因此绑定在运行时失败.
and naturally, InkStringViewModel
does not have a property called text
, so the binding fails at runtime.
这篇关于如何在 MVVM 框架中正确绑定到用户控件的依赖属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!