如何在MVVM框架中正确绑定用户控件的依赖属性 [英] How to correctly bind to a dependency property of a usercontrol in a MVVM framework

查看:562
本文介绍了如何在MVVM框架中正确绑定用户控件的依赖属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直无法找到一个干净,简单的例子,说明如何正确地实现具有MVVM框架中的依赖属性的WPF的用户控件。我的代码下面失败,每当我分配usercontrol一个数据文本。



我试图:


  1. 从调用的ItemsControl设置依赖属性,

  2. 将该依赖属性的值设置为被调用的用户控件的ViewModel可用。

我还有很多学习,真诚感谢任何帮助。



这是最大的usercontrol中的ItemsControl那是通过依赖属性TextInControl(来自另一个问题的示例)调用InkStringView用户控件。

 < ItemsControl 
ItemsSource ={Binding Strings}x:Name =self>

< ItemsControl.ItemsPanel>
< ItemsPanelTemplate>
< StackPanel Horizo​​ntalAlignment =LeftVerticalAlignment =TopOrientation =Vertical/>
< / ItemsPanelTemplate>
< /ItemsControl.ItemsPanel>


< ItemsControl.ItemTemplate>
< DataTemplate>
< DataTemplate.Resources>
< Style TargetType =v:InkStringView>
< Setter Property =FontSizeValue =25/>
< Setter Property =Horizo​​ntalAlignmentValue =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 的主要方法


  1. 可以在任何地方使用独立的 UserControl code> DataContext 被要求。



    这种类型的 UserControl 通常会向任何人显示 DependencyProperties 值要使用,如下所示:

     < v:InkStringView TextInControl ={Binding SomeValue}/> ; 

    我可以想到的典型例子是任何通用的例子,如Calendar控件或Popup控件。 p>


  2. 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:

  1. Set the dependency property from the calling ItemsControl , and
  2. 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

  1. A standalone UserControl that can be used anywhere without a specific DataContext being required.

    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.

  2. A UserControl that is meant to be used with a specific Model or ViewModel 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 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}" />
    

    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

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆