为什么我的带有数据触发器的自定义 ContentControl 没有显示在 UI 中,尽管调用了构造函数?

Why is my custom ContentControl with data triggers not showing up in the UI, though the constructor is called?

我正在使用 C# 中的反射树和表达式树为我们的数据库构建一个适应性很强的搜索工具。因此,我需要一个自定义 ContentControl - 称为 'MultiStyleInputBox' - 它使用数据触发器将其 ContentTemplate 调整为预期的输入类型。问题是,虽然代码构建得很好并且我已经确认在代码执行时 public 和静态构造函数都被命中,但 ContentControl 的内容根本没有出现在我的 UI 中.

现在,我对编写自定义 XAML/C# UI 控件 classes 比较陌生,但我已经能够拼凑出以下内容:

<ContentControl x:Class="MyApp.MultiStyleInputBox"
             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"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             Name="multiStyleInputBox">
    <ContentControl.Resources>
        <Style TargetType="ContentControl" BasedOn="{StaticResource {x:Type ContentControl}}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding InputType, ElementName=multiStyleInputBox}" Value="{x:Type sys:DateTime}">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <DatePicker SelectedDate="{Binding Value, ElementName=multiStyleInputBox}"/>
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
               <!--I have several of these triggers for different data types-->
            </Style.Triggers>
            <Style.Setters>
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <TextBox Text="{Binding Value, ElementName=multiStyleInputBox}"/>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style.Setters>
        </Style>
    </ContentControl.Resources>
</ContentControl>

后面的代码:

    public sealed partial class MultiStyleInputBox : ContentControl
    {
        //Dependency properties
        public Type InputType
        {
            get { return (Type)GetValue(InputTypeProperty); }
            set { SetValue(InputTypeProperty, value); }
        }
        public static readonly DependencyProperty InputTypeProperty =
            DependencyProperty.Register("InputType", typeof(Type), typeof(MultiStyleInputBox));
        public object Value
        {
            get { return (object)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(object), typeof(MultiStyleInputBox));

        //Constructors
        public MultiStyleInputBox() : base()
        {

        }
        static MultiStyleInputBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiStyleInputBox), 
                new FrameworkPropertyMetadata(typeof(MultiStyleInputBox)));
        }
    }

我一度认为我可能没有设置ContentControl的内容,所以我添加了一个<ContentPresenter/>,但是我收到一个错误说内容被设置了不止一次,所以我相信我的 <Style.Setters></Style.Setters> 部分正在处理这个问题。否则,即使 运行 在我的绑定上使用 PresentationTraceSources.TraceLevel="High",到目前为止我还没有能够 运行 进入任何有用的错误。

我的代码中是否存在某种我可以立即解决(希望如此)的明显问题?我需要重新评估我解决问题的方法吗?


更新

在下面的答案中建议更正后,这里是最新版本的代码:

<ContentControl x:Class="MyApp.MultiStyleInputBox"
             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"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             Name="multiStyleInputBox">
    <ContentControl.Style>
        <Style TargetType="ContentControl" BasedOn="{StaticResource {x:Type ContentControl}}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding InputType, ElementName=multiStyleInputBox, Value="{x:Type sys:DateTime}">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <DatePicker SelectedDate="{Binding Value, ElementName=multiStyleInputBox}"/>
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
                <!--I have several of these triggers for different data types-->
            </Style.Triggers>
            <Style.Setters>
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <TextBox Text="{Binding Value, ElementName=multiStyleInputBox}"/>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style.Setters>
        </Style>
    </ContentControl.Style>
</ContentControl>

和代码隐藏:

public partial class MultiStyleInputBox : ContentControl
    {
        //Dependency properties
        public Type InputType
        {
            get { return (Type)GetValue(InputTypeProperty); }
            set { SetValue(InputTypeProperty, value); }
        }
        public static readonly DependencyProperty InputTypeProperty =
            DependencyProperty.Register("InputType", typeof(Type), typeof(MultiStyleInputBox));
        public object Value
        {
            get { return GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(object), typeof(MultiStyleInputBox),
                new FrameworkPropertyMetadata(DateTime.Now,
                    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        //Constructors
        public MultiStyleInputBox() : base()
        {

        }
        static MultiStyleInputBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiStyleInputBox), 
                new FrameworkPropertyMetadata(typeof(MultiStyleInputBox)));
        }
    }

这是 MultiStyleInputBox 的测试实例(我正在使用 Mahapps.Metro):

<Controls:MetroWindow
    xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
    x:Class="MyApp.TestWindow"
        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:MyApp"
        Title="Test Window" Height="450" Width="800"
        xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <StackPanel>
        <local:MultiStyleInputBox x:Name="TestMultiBox" Value="1" InputType="{x:Type sys:Int32}"/>
    </StackPanel>
</Controls:MetroWindow>

当我尝试实例化这个 class 时,我的 UI 中仍然没有显示任何内容,而且 ContentControl 也没有占用任何 space。即使我包含 Width="50" Height="24",我仍然一无所获。我测试了在代码隐藏中同时设置 Value 和 InputType 并使用断点检查对象,我发现,虽然两个值都已设置,但 ContentControl 的 Content 仍然为空。

眼前的问题是您的样式没有应用到您要应用它的 ContentControl。您正在定义一个隐式 ContentControl 样式,该样式将应用于您在此控件的内容中创建的任何 ContentControls——但您没有创建任何内容控件,无论如何这都不是您想要的。

为了快速修复,只需将 ContentControl.Resources 更改为 ContentControl.Style

<ContentControl x:Class="MyApp.MultiStyleInputBox"
             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"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             Name="multiStyleInputBox">
    <ContentControl.Style>
        <Style TargetType="ContentControl" BasedOn="{StaticResource {x:Type ContentControl}}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding InputType, ElementName=multiStyleInputBox}" Value="{x:Type sys:DateTime}">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <DatePicker SelectedDate="{Binding Value, ElementName=multiStyleInputBox}"/>
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
               <!--I have several of these triggers for different data types-->
            </Style.Triggers>
            <Style.Setters>
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <TextBox Text="{Binding Value, ElementName=multiStyleInputBox}"/>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style.Setters>
        </Style>
    </ContentControl.Style>
</ContentControl>

您的下一个问题是,在 DatePicker 中选择新的 DateTime 不会更新绑定到视图模型的 Value 属性 的 属性。这是解决方法:

public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register("Value", typeof(object), typeof(MultiStyleInputBox),
        new FrameworkPropertyMetadata(DateTime.Now, 
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

最后一个问题(或者可能是第一个)是您没有在构造函数中调用 InitializeComponent(),这在任何 WPF 代码隐藏中始终是必需的 class:

    public MultiStyleInputBox()
    {
        InitializeComponent();
    }