我的WPF自定义控件的数据上下文为取代父母的 [英] My WPF custom control's Data Context is superseding parent's

查看:368
本文介绍了我的WPF自定义控件的数据上下文为取代父母的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的主窗口中,我尝试绑定到一个布尔值。但它在寻找我的自定义控件的数据上下文来代替。如果我没有在用户控件的DataContext赋值,然后在主窗口的绑定工作,但(显然)这个刹车在用户控件绑定。这里的错误:

  System.Windows.Data错误:40:BindingEx pression路径错误:'MyControlVisible'属性不是'对象'''MyUserControlModel(哈希code =发现1453241)。 BindingEx pression:路径= MyControlVisible;的DataItem ='MyUserControlModel'(哈希code = 1453241);目标元素是'的MyUserControl'(名称='_的MyUserControl'); target属性是'能见度'(类型'能见度')

我需要绑定到两个控制工作,但我不希望用户控件的DataContext,以取代窗口的。

这里的code:

 <窗​​口x:类=Sandbox.MainWindow
        的xmlns =htt​​p://schemas.microsoft.com/winfx/2006/xaml/$p$psentation
        的xmlns:X =htt​​p://schemas.microsoft.com/winfx/2006/xaml
        的xmlns:控制=CLR的命名空间:Sandbox.Controls标题=沙盒>
    < D​​ockPanel中LastChildFill =真>
        < D​​ockPanel.Resources>
            < BooleanToVisibilityConverter X:键=boolToVis/>
        < /DockPanel.Resources>
        <网格和GT;
            <控制:的MyUserControl X:名称=_的MyUserControl能见度={结合MyControlVisible,转换器= {StaticResource的boolToVis}}/>
        < /网格和GT;
    < / DockPanel中>
< /窗GT;命名空间沙盒
{
    公共部分类主窗口
    {
        私人MainWindowModel模型;
        公共主窗口()
        {
            的InitializeComponent();
            的DataContext = =模型新MainWindowModel();
            _myUserControl.Initialize(model.MyUControlModel);
        }
    }
}使用System.ComponentModel;
使用Sandbox.Controls;命名空间沙盒
{
    公共类MainWindowModel:BaseModel
    {
        公共MyUserControlModel MyUControlModel {搞定;组; }
        公共BOOL MyControlVisible {搞定;组; }
        公共MainWindowModel()
        {
            MyUControlModel =新MyUserControlModel();
            MyControlVisible = FALSE;
            的OnChange();
        }
    }    公共类BaseModel:INotifyPropertyChanged的
    {
        公共事件PropertyChangedEventHandler的PropertyChanged;
        保护无效的OnChange(字符串s)
        {
            VAR处理器=的PropertyChanged;
            如果(处理!= NULL)
            {
                处理程序(在此,新PropertyChangedEventArgs(S));
            }
        }
    }
}<用户控件X:类=Sandbox.Controls.MyUserControl
             的xmlns =htt​​p://schemas.microsoft.com/winfx/2006/xaml/$p$psentation
             的xmlns:X =htt​​p://schemas.microsoft.com/winfx/2006/xaml
             的xmlns:MC =htt​​p://schemas.openxmlformats.org/markup-compatibility/2006
             的xmlns:D =htt​​p://schemas.microsoft.com/ex$p$pssion/blend/2008
             MC:可忽略=D>
    <网格和GT;
        < TextBlock的文本={结合MyBoundText}/>
    < /网格和GT;
< /用户控件>命名空间Sandbox.Controls
{
    公共部分类的MyUserControl
    {
        公众的MyUserControl()
        {
            的InitializeComponent();
        }        公共无效初始化(MyUserControlModel上下文)
        {
            DataContext的情况下=;
        }
    }}命名空间Sandbox.Controls
{
    公共类MyUserControlModel:BaseModel
    {
        公共字符串MyBoundText {搞定;组; }
        公共MyUserControlModel()
        {
            MyBoundText =的Hello World!;
            的OnChange();
        }
    }
}


解决方案

这是你永远不应该设置的DataContext 直接从<$ C $众多原因之一C>用户控件本身。

当你这样做,你不能再使用任何其他的DataContext 用它,因为用户控件的的DataContext 难$ C $光盘

在你结合的情况下,通常是的DataContext 将被继承,在能见度结合能找到的财产 MyControlVisible 的电流的DataContext ,但是因为你很难codeD上的的DataContext 在你的用户控件的构造,即未找到属性。

您可以在你的绑定指定一个不同的结合来源,如

 &LT;控制:可见的MyUserControl ={结合
    的RelativeSource = {的RelativeSource AncestorType = {X:类型窗口}},
    路径= DataContext.MyControlVisible,
    转换器= {StaticResource的boolToVis}}... /&GT;

不过,这只是为这个特殊情况下,问题解决方法是,在我看来,不是长久之计。一个更好的解决办法是不难code中的的DataContext 用户控件

有几种不同的方式,您可以根据您的用户控件的目的和应用程序如何被设计做的。


  • 您可以在您的用户控件创建一个的DependencyProperty 传递的价值,并绑定到。

     &LT;控制:UcModel的MyUserControl ={结合MyUControlModelProperty}... /&GT;

     &LT;用户控件X:类=Sandbox.Controls.MyUserControl
                 的ElementName = ...的MyUserControl GT&;
        &LT;电网的DataContext ={结合UCModel,的ElementName =}的MyUserControl&GT;
            &LT; TextBlock的文本={结合MyBoundText}/&GT;
        &LT; /网格和GT;
    &LT; /用户控件&GT;


  • 或者你可以建立你的用户控件并期望一个特定的属性将得到传递给它在的DataContext 。这通常是我做的,结合的DataTemplates

     &LT;控制:的DataContext的MyUserControl ={结合MyUControlModelProperty}... /&GT;

     &LT;用户控件X:类=Sandbox.Controls.MyUserControl...&GT;
        &LT;网格和GT;
            &LT; TextBlock的文本={结合MyBoundText}/&GT;
        &LT; /网格和GT;
    &LT; /用户控件&GT;


  • 正如我前面所说,我喜欢用的DataTemplates 来显示我的用户控件意想不到特定键入模式他们的DataContext ,所以通常我主窗口XAML会是这个样子:

     &LT;数据类型的DataTemplate ={X:类型本地:MyUControlModel}&GT;
        &LT;控制:的MyUserControl /&GT;
    &LT; / DataTemplate中&GT;&lt;内容presenter CONTENT ={结合MyUControlModelProperty}... /&GT;


In my main window, I try to bind to a bool. But it's looking in my custom control's data context instead. If I don't assign DataContext in the user control, then the main window's bindings work, but (obviously) this brakes the bindings in the user control. Here's the error:

System.Windows.Data Error: 40 : BindingExpression path error: 'MyControlVisible' property not found on 'object' ''MyUserControlModel' (HashCode=1453241)'. BindingExpression:Path=MyControlVisible; DataItem='MyUserControlModel' (HashCode=1453241); target element is 'MyUserControl' (Name='_myUserControl'); target property is 'Visibility' (type 'Visibility')

I need binding to work on both controls, but I don't want the user control's datacontext to supersede the window's.

Here's the code:

<Window x:Class="Sandbox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Controls="clr-namespace:Sandbox.Controls" Title="Sandbox">
    <DockPanel LastChildFill="True">
        <DockPanel.Resources>
            <BooleanToVisibilityConverter x:Key="boolToVis" />
        </DockPanel.Resources>
        <Grid>
            <Controls:MyUserControl x:Name="_myUserControl" Visibility="{Binding MyControlVisible, Converter={StaticResource boolToVis}}"/>
        </Grid>
    </DockPanel>
</Window>

namespace Sandbox
{
    public partial class MainWindow
    {
        private MainWindowModel model;
        public MainWindow()
        {
            InitializeComponent();
            DataContext = model = new MainWindowModel();
            _myUserControl.Initialize(model.MyUControlModel);
        }
    }
}

using System.ComponentModel;
using Sandbox.Controls;

namespace Sandbox
{
    public class MainWindowModel : BaseModel
    {
        public MyUserControlModel MyUControlModel { get; set; }
        public bool MyControlVisible { get; set; }
        public MainWindowModel()
        {
            MyUControlModel = new MyUserControlModel();
            MyControlVisible = false;
            OnChange("");
        }
    }

    public class BaseModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnChange(string s)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(s));
            }
        }
    }
}

<UserControl x:Class="Sandbox.Controls.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" 
             mc:Ignorable="d">
    <Grid>
        <TextBlock Text="{Binding MyBoundText}"/>
    </Grid>
</UserControl>

namespace Sandbox.Controls
{
    public partial class MyUserControl
    {
        public MyUserControl()
        {
            InitializeComponent();
        }

        public void Initialize(MyUserControlModel context)
        {
            DataContext = context;
        }
    }

}

namespace Sandbox.Controls
{
    public class MyUserControlModel : BaseModel
    {
        public string MyBoundText { get; set; }
        public MyUserControlModel()
        {
            MyBoundText = "Hello World!";
            OnChange("");
        }
    }
}

解决方案

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 in.

In the case of your binding, normally the DataContext would be inherited so the Visibility binding could find the property MyControlVisible on the current DataContext, however because you hardcoded the DataContext in your UserControl's constructor, that property is not found.

You could specify a different binding source in your binding, such as

<Controls:MyUserControl Visibility="{Binding 
    RelativeSource={RelativeSource AncestorType={x:Type Window}}, 
    Path=DataContext.MyControlVisible, 
    Converter={StaticResource boolToVis}}" ... />

However that's just a workaround for the problem for this specific case, and in my view is not a permanent solution. A better solution is to simply not hardcode the DataContext in your UserControl

There are a few different ways you can do depending on your UserControl's purpose and how your application is designed.

  • You could create a DependencyProperty on your UserControl to pass in the value, and bind to that.

    <Controls:MyUserControl UcModel="{Binding MyUControlModelProperty}" ... />
    

    and

    <UserControl x:Class="Sandbox.Controls.MyUserControl"
                 ElementName=MyUserControl...>
        <Grid DataContext="{Binding UCModel, ElementName=MyUserControl}">
            <TextBlock Text="{Binding MyBoundText}"/>
        </Grid>
    </UserControl>
    

  • Or you could build your UserControl with the expectation that a specific property will get passed to it in the DataContext. This is normally what I do, in combination with DataTemplates.

    <Controls:MyUserControl DataContext="{Binding MyUControlModelProperty}" ... />
    

    and

    <UserControl x:Class="Sandbox.Controls.MyUserControl"...>
        <Grid>
            <TextBlock Text="{Binding MyBoundText}"/>
        </Grid>
    </UserControl>
    

  • As I said above, I like to use DataTemplates to display my UserControls that expect a specific type of Model for their DataContext, so typically my XAML for the main window would look something like this:

    <DataTemplate DataType="{x:Type local:MyUControlModel}">
        <Controls:MyUserControl />
    </DataTemplate>
    
    <ContentPresenter Content="{Binding MyUControlModelProperty}" ... />
    

这篇关于我的WPF自定义控件的数据上下文为取代父母的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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