【问题标题】:Why DataTemplate return class name instead of control?为什么 DataTemplate 返回类名而不是控件?
【发布时间】:2016-09-08 23:38:05
【问题描述】:

例如,我想创建自定义控件添加属性 Info 和 InfoTemplate。
我使用 ContetnPresenter 为 Info 属性在 generic.xaml 中定义 ControlTemplate。
当我不使用 InfoTemplate 时它可以正常工作,但是当我应用 ItemTemplate 时,内容表示为类名字符串。应用于 GroupBox 的相同模板按预期工作。我错了什么?我需要在 OnApplyTemplate 中添加一些额外的代码吗?
贝娄是我的应用程序和来源的打印屏幕。红色边框是 GroupBox,蓝色是我的 Control。绿色边框是 DataTemplate 的一部分。

编辑: 为了测试我创建类 MyGroupBox 继承形式 GroupBox 并覆盖方法 OnHeaderChanged

public class MyGroupBox : GroupBox
{
    protected override void OnHeaderChanged(object oldHeader, object newHeader)
    {
        //base.OnHeaderChanged(oldHeader, newHeader);
    }
}

在这种情况下,GroupBox.Heder 的行为就像我的 MyCustomControl 并显示文本而不是控件。所以问题是:我应该在我的控制事件中实现什么才能像我想要的那样工作?


MyCustomControl.cs

using System.Windows;
using System.Windows.Controls;

namespace WpfApplication7
{

    public class MyCustomControl : ContentControl
    {
        static MyCustomControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
        }

        public object Info
        {
            get { return (object)GetValue(InfoProperty); }
            set { SetValue(InfoProperty, value); }
        }

        public DataTemplate InfoTemplate
        {
            get { return (DataTemplate)GetValue(InfoTemplateProperty); }
            set { SetValue(InfoTemplateProperty, value); }
        }

        public static readonly DependencyProperty InfoProperty =
            DependencyProperty.Register(nameof(Info), typeof(object), typeof(MyCustomControl), new PropertyMetadata(null));

        public static readonly DependencyProperty InfoTemplateProperty =
            DependencyProperty.Register(nameof(InfoTemplate), typeof(DataTemplate), typeof(MyCustomControl), new PropertyMetadata(null));
    }
}

Generic.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication7">

    <Style TargetType="{x:Type local:MyCustomControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:MyCustomControl}">
                    <StackPanel>

                        <TextBlock FontWeight="Bold">Info</TextBlock>
                        <ContentPresenter ContentSource="Info"/>

                        <TextBlock FontWeight="Bold">Content</TextBlock>
                        <ContentPresenter/>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

MainWindow.xml

<Window x:Class="WpfApplication7.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication7"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        DATA_CONTEXT
    </Window.DataContext>
    <Window.Resources>
        <DataTemplate x:Key="dataTemplate">
            <Border BorderBrush="Green" BorderThickness="5">
                <ContentPresenter Content="{Binding}"/>
            </Border>
        </DataTemplate>
    </Window.Resources>
    <StackPanel>

        <Border BorderBrush="Red" BorderThickness="4">
            <GroupBox HeaderTemplate="{StaticResource dataTemplate}">
                <GroupBox.Header>
                    <TextBlock Text="{Binding}"/>
                </GroupBox.Header>
            </GroupBox>
        </Border>

        <Border BorderBrush="Blue" BorderThickness="4">
            <local:MyCustomControl InfoTemplate="{StaticResource dataTemplate}">
                <local:MyCustomControl.Info>
                    <TextBlock Text="{Binding}"/>
                </local:MyCustomControl.Info>

                My content
            </local:MyCustomControl>
        </Border>
    </StackPanel>
</Window>

MainWindow.xaml.cs

using System.Collections.Generic;
using System.Windows;


namespace WpfApplication7
{

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

【问题讨论】:

    标签: c# wpf xaml datatemplate


    【解决方案1】:

    所以放弃我的答案并重新开始,因为我对所要求的内容有了更好的理解。这里的想法是基本上重新创建自定义GroupBox 类似控件。问题是自定义控件中Info 属性的DataContext(基本上是GroupBoxHeader 属性)没有像自定义控件本身那样出现DataContext当使用 GroupBox 时。

    所以问题是您设置为 Info 属性的 UI 块永远不会添加为控件的逻辑子级,因此它不会以继承 DataContext 的方式添加为在GroupBox 中使用相同的代码时会发生这种情况。为此,只需将您的自定义控件类更新为以下内容:

    public class MyCustomControl : ContentControl
        {
            static MyCustomControl()
            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));  
            }
    
            public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
            }
    
            public object Info
            {
                get { return (object)GetValue(InfoProperty); }
                set { SetValue(InfoProperty, value); }
            }
    
            public DataTemplate InfoTemplate
            {
                get { return (DataTemplate)GetValue(InfoTemplateProperty); }
                set { SetValue(InfoTemplateProperty, value); }
            }
    
            public static readonly DependencyProperty InfoProperty =
                DependencyProperty.Register(nameof(Info), typeof(object), typeof(MyCustomControl), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(MyCustomControl.OnHeaderChanged)));
    
            private static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var obj = (MyCustomControl)d; 
                obj.RemoveLogicalChild(e.OldValue);
                obj.AddLogicalChild(e.NewValue);
            }
    
            public static readonly DependencyProperty InfoTemplateProperty =
                DependencyProperty.Register(nameof(InfoTemplate), typeof(DataTemplate), typeof(MyCustomControl), new PropertyMetadata(null));
        }
    

    这将解决问题,但是,应该说明的是,从 HeaderedContentControl 派生而不是从 ContentControl 派生可能会更干净,因为 HeaderedContentControl 已经为您准备好了所有这些设置盒子里还有一个 HeaderHeaderTemplate 可以用来代替 InfoInfoTemplate 属性,这样可以节省一些代码。

    如果您想在不考虑逻辑子代等问题的情况下使其正常工作,您只需更新您设置为 Info 的 UI 块中的绑定,并让它使用搜索自定义控件的 RelativeSource祖先,然后有一个“DataContext”路径,它将手动覆盖整个问题,尽管您必须记住每次都这样做。

    我敢打赌,您最好从 HeaderedContentControl 派生,看起来应该已经包含您正在寻找的所有功能。

    【讨论】:

    • 那不完全工作。如果我喜欢你写的,那么 Info TextBlock 上没有绿色边框。当我不设置 InfoTemplate 时,此行为是相同的。我的 generic.xaml 类似于在 Blend 中生成的 GroupBox 的 ControlTemplate。
    • 如果您想保留绿色边框,那么您应该将 ContentTemplate="{TemplateBinding InfoTemplate}" 添加到我在答案中包含的 ContentPresenter 行中。正如我在回答中引用的文章中所解释的那样,您的 ContentSource 之前已经设置了这个(以及其他属性)。
    • 我知道 ContentSource 是如何工作的。您是否尝试使用您的更改运行我的代码?这对我不起作用,或者我做错了什么。
    • 对不起,我说错了。您应该只拥有该更新并刮擦另一个。因此,您的 generic.xaml 中应该有 &lt;ContentPresenter ContentTemplate="{TemplateBinding InfoTemplate}"/&gt;Content 绑定会覆盖 ContentTemplate,这就是您再次看到 TextBlock.ToString 结果的原因。对我来说,我看到“我的内容”带有绿色边框,所以我猜这就是你要找的。如果不让我知道。此外,另一个ContentPresenter 也显示“我的内容”,因为那里正在使用默认模板。
    • 没有。我想看到带有绿色边框的“DATA_CONTEXT”,因为它是为Info 属性(&lt;local:MyCustomControl.Info&gt; &lt;TextBlock Text="{Binding}"/&gt;&lt;/local:MyCustomControl.Info&gt;)定义的
    猜你喜欢
    • 1970-01-01
    • 2014-04-09
    • 1970-01-01
    • 1970-01-01
    • 2015-11-02
    • 2021-09-13
    • 2017-06-30
    • 1970-01-01
    • 2012-11-19
    相关资源
    最近更新 更多