Wpf 自定义控件:如何实现 CustomContent

Wpf Custom Control: How to implement CustomContent

我正在 WPF 中开发 CustomControl。我想实现一个 CardControl。我有两个内容 "Front" 和 "Back",我想 在它们之间翻转 。 但是我无法在样式中定义工作触发器来在内容之间翻转...

首先我创建了一个自定义控件,具有 DependencyProperties:

public class Card : Control {

        #region initializer

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

        #endregion

        #region dependencyProperties

        public FrameworkElement CustomContent {
            get { return (FrameworkElement)GetValue(CustomContentProperty); }
            set { SetValue(CustomContentProperty, value); }
        }
        public static DependencyProperty CustomContentProperty =
            DependencyProperty.Register(nameof(CustomContent), typeof(FrameworkElement), typeof(Card), new PropertyMetadata());

        public FrameworkElement Front {
            get { return (FrameworkElement)GetValue(FrontProperty); }
            set { SetValue(FrontProperty, value); }
        }
        public static DependencyProperty FrontProperty =
            DependencyProperty.Register(nameof(Front), typeof(FrameworkElement), typeof(Card), new PropertyMetadata());

        public FrameworkElement Back {
            get { return (FrameworkElement)GetValue(BackProperty); }
            set { SetValue(BackProperty, value); }
        }
        public static DependencyProperty BackProperty =
            DependencyProperty.Register(nameof(Back), typeof(FrameworkElement), typeof(Card), new PropertyMetadata());



        public bool IsDetailed {
            get { return (bool)GetValue(IsDetailedProperty); }
            set { SetValue(IsDetailedProperty, value); }
        }
        public static readonly DependencyProperty IsDetailedProperty =
            DependencyProperty.Register(nameof(IsDetailed), typeof(bool), typeof(Card), new PropertyMetadata(false));



        public CornerRadius CornerRadius {
            get { return (CornerRadius)GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }
        public static readonly DependencyProperty CornerRadiusProperty =
            DependencyProperty.Register(nameof(CornerRadius), typeof(CornerRadius), typeof(Card));

        #endregion

然后我在Generic.xaml中定义了Style:

<Style TargetType="{x:Type local:Card}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:Card}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="{TemplateBinding CornerRadius}">
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"
                                          Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:Card}}, Path=CustomContent, 
                                            Converter={StaticResource Debugger}, UpdateSourceTrigger=PropertyChanged}"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource 
                                        AncestorType={x:Type local:Card}}, 
                                        Path=isDetailed, 
                                        Converter={StaticResource Debugger}}" 
                                     Value="True">
                            <Setter Property="CustomContent" 
                                    Value="{Binding RelativeSource={RelativeSource 
                                        AncestorType={x:Type local:Card}}, 
                                        Path=Back}"/>
                        </DataTrigger>

                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource 
                                        AncestorType={x:Type local:Card}}, 
                                        Path=isDetailed}" 
                                     Value="False">

                            <Setter Property="CustomContent" 
                                    Value="{Binding RelativeSource={RelativeSource 
                                        AncestorType={x:Type local:Card}}, 
                                        Path=Front}"/>
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

并且我使用一个按钮实现了一个用于测试目的的简单 CardControl,它翻转了 isDetailed 状态:

<StackPanel>
    <CustomCharts:Card Name="TestCard">
        <CustomCharts:Card.CustomContent>
            <TextBlock Text="Hello World!"/>
        </CustomCharts:Card.CustomContent>
        <CustomCharts:Card.Front>
            <TextBlock Text="Wow vordere sache!"/>
        </CustomCharts:Card.Front>
        <CustomCharts:Card.Back>
            <TextBlock Text="Wow hintere sache!"/>
        </CustomCharts:Card.Back>
    </CustomCharts:Card>

    <Button Click="Button_Click" Width="50" Height="20" Content="Test"/>
</StackPanel>
private void Button_Click( object sender, RoutedEventArgs e ) {
    this.TestCard.IsDetailed= !this.TestCard.IsDetailed;
}

感谢任何帮助,我被卡住了...

如果使用 object 类型而不是 FrameworkElement 声明 FrontBack,您会受益匪浅 - 主要是因为它可以轻松出价。如果为这两个属性添加相应的模板,您可以保持丰富的视觉外观。

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

    public object Front
    {
        get { return (object)GetValue(FrontProperty); }
        set { SetValue(FrontProperty, value); }
    }

    public static readonly DependencyProperty FrontProperty =
        DependencyProperty.Register("Front", typeof(object), typeof(Card), new PropertyMetadata(null));

    public DataTemplate FrontTemplate
    {
        get { return (DataTemplate)GetValue(FrontTemplateProperty); }
        set { SetValue(FrontTemplateProperty, value); }
    }

    public static readonly DependencyProperty FrontTemplateProperty =
        DependencyProperty.Register("FrontTemplate", typeof(DataTemplate), typeof(Card), new PropertyMetadata(null));

    public object Back
    {
        get { return (object)GetValue(BackProperty); }
        set { SetValue(BackProperty, value); }
    }

    public static readonly DependencyProperty BackProperty =
        DependencyProperty.Register("Back", typeof(object), typeof(Card), new PropertyMetadata(null));

    public DataTemplate BackTemplate
    {
        get { return (DataTemplate)GetValue(BackTemplateProperty); }
        set { SetValue(BackTemplateProperty, value); }
    }

    public static readonly DependencyProperty BackTemplateProperty =
        DependencyProperty.Register("BackTemplate", typeof(DataTemplate), typeof(Card), new PropertyMetadata(null));

    public bool IsDetailed
    {
        get { return (bool)GetValue(IsDetailedProperty); }
        set { SetValue(IsDetailedProperty, value); }
    }

    public static readonly DependencyProperty IsDetailedProperty =
        DependencyProperty.Register(nameof(IsDetailed), typeof(bool), typeof(Card), new PropertyMetadata(false));

    public CornerRadius CornerRadius
    {
        get { return (CornerRadius)GetValue(CornerRadiusProperty); }
        set { SetValue(CornerRadiusProperty, value); }
    }
    public static readonly DependencyProperty CornerRadiusProperty =
        DependencyProperty.Register(nameof(CornerRadius), typeof(CornerRadius), typeof(Card), new PropertyMetadata(new CornerRadius(0)));
}

在 IsDetailed 上使用触发器 属性 前后翻转:

<Style TargetType="{x:Type local:Card}">
    <Setter Property="Background" Value="Khaki"/>
    <Setter Property="BorderBrush" Value="Black"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="CornerRadius" Value="10"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:Card}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        CornerRadius="{TemplateBinding CornerRadius}">
                    <ContentPresenter x:Name="presenter" 
                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Border>

                <ControlTemplate.Triggers>

                    <Trigger Property="IsDetailed" Value="True">
                        <Setter TargetName="presenter" Property="Content" 
                                Value="{Binding Front, RelativeSource={RelativeSource TemplatedParent}}"/>
                        <Setter TargetName="presenter" Property="ContentTemplate"
                                Value="{Binding FrontTemplate, RelativeSource={RelativeSource TemplatedParent}}"/>
                    </Trigger>

                    <Trigger Property="IsDetailed" Value="False">
                        <Setter TargetName="presenter" Property="Content" 
                                Value="{Binding Back, RelativeSource={RelativeSource TemplatedParent}}"/>
                        <Setter TargetName="presenter" Property="ContentTemplate"
                                Value="{Binding BackTemplate, RelativeSource={RelativeSource TemplatedParent}}"/>
                    </Trigger>                        
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

在 Template 中使用 TemplateBindings,在 Setters 中使用具有正常绑定的 RelativeSource TemplatedParent(Setter 中不支持 TemplateBinding)。

这里有两个使用示例 - 有和没有绑定:

<StackPanel DataContext="{x:Static sys:DateTime.Now}">

    <CheckBox Name="chk_1"/>
    <local:Card Front="Front" 
                Back="Back" 
                IsDetailed="{Binding IsChecked, ElementName=chk_1}"/>


    <CheckBox Name="chk_2"/>
    <local:Card Front="{Binding DayOfWeek}" 
                Back="{Binding TimeOfDay}" 
                IsDetailed="{Binding IsChecked, ElementName=chk_2}">
        <local:Card.FrontTemplate>
            <DataTemplate>
                <TextBlock Foreground="Red" FontWeight="Bold" FontSize="20" 
                           Text="{Binding}" TextDecorations="Underline"/>
            </DataTemplate>
        </local:Card.FrontTemplate>

        <local:Card.BackTemplate>
            <DataTemplate>
                <TextBlock Foreground="Blue" FontWeight="Bold" FontSize="20" 
                           Text="{Binding}" TextDecorations="Underline"/>
            </DataTemplate>
        </local:Card.BackTemplate>
    </local:Card>

</StackPanel>

结果: