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
声明 Front
和 Back
,您会受益匪浅 - 主要是因为它可以轻松出价。如果为这两个属性添加相应的模板,您可以保持丰富的视觉外观。
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>
结果:
我正在 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
声明 Front
和 Back
,您会受益匪浅 - 主要是因为它可以轻松出价。如果为这两个属性添加相应的模板,您可以保持丰富的视觉外观。
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>
结果: