为什么DataTemplate返回类名而不是控件?

3
我希望创建自定义控件并添加Info和InfoTemplate属性。
我在generic.xaml中使用ContentPresenter为Info属性定义ControlTemplate。
当我不使用InfoTemplate时,它能正常工作,但是当我应用ItemTemplate时,内容会表示为类名字符串。同样的模板应用于GroupBox时则像预期一样工作。我做错了什么?我需要在OnApplyTemplate中添加一些额外的代码吗?
下面是我的应用程序和源代码的打印屏幕。红色边框是GroupBox,蓝色是我的控件。绿色边框是DataTemplate的一部分。

App print-screen

编辑: 为了测试,我创建了一个名为MyGroupBox的类,继承自GroupBox,并重写了OnHeaderChanged方法。

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

在这种情况下,GroupBox.Header的行为类似于我的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();
        }
    }
}
1个回答

2
因为更好地理解了被请求的内容,现在我打算放弃以前的答案,重新开始。这里的想法是基本上重新创建一个类似于自定义 GroupBox 的控件。问题是自定义控件中 Info 属性(基本上是 GroupBoxHeader 属性)的 DataContext 不像使用 GroupBox 时一样成为自定义控件本身的 DataContext
因此,问题在于将设置为 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派生,看起来应该已经包含了您正在寻找的所有功能。

那不完全工作。如果我按照您的写法操作,那么信息 TextBlock 上就没有绿色边框。当我不设置 InfoTemplate 时,此行为是相同的。我的 generic.xaml 类似于在 Blend 中生成的 GroupBox 的 ControlTemplate。 - siwydym67
我现在明白你想要什么了,但为了确保,我将我认为你正在寻找的截图作为答案的编辑放在了里面。请告诉我这张图片是否是你想要的结果。 - Chiune Sugihara
是的,那是我要找的。 - siwydym67
好的,我根据更好地理解你的问题重新回答了。请告诉我你的想法。 - Chiune Sugihara
让我们在聊天室中继续这个讨论 - siwydym67
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接