Conditional XAML (WPF)
I am trying to create a User Control that, depending on the mode the user sets in the Dependency Property, changes the User Control to either a TextBlock and another TextBlock or a TextBlock and a TextBox. I know the dependency properties are getting the information, but the problem arises when I try to set the correct template. For some reason, the template does not render correctly.
XAML:
<UserControl
x:Class="BookOrganizer.FlipBox"
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:my="clr-namespace:BookOrganizer"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Orientation="Horizontal" Height="Auto" Width="Auto" >
<StackPanel.Resources>
<ContentControl x:Key="Box">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Title}" Height="Auto" Width="Auto" />
<TextBox Text="{Binding Path=Text}" Height="Auto" Width="Auto" />
</StackPanel>
</ContentControl>
<ContentControl x:Key="Block" Height="Auto" Width="Auto">
<StackPanel Orientation="Horizontal" Height="Auto" Width=&开发者_运维问答quot;Auto">
<TextBlock Text="{Binding Path=Title}" Height="Auto" Width="Auto" />
<TextBlock Text="{Binding Path=Text}" Height="Auto" Width="Auto"/>
</StackPanel>
</ContentControl>
</StackPanel.Resources>
<ContentControl Template="{Binding Path=BoxMode}" />
</StackPanel>
</UserControl>
Code Behind:
using System;
using System.Windows;
using System.Windows.Controls;
namespace BookOrganizer
{
/// <summary>
/// Interaction logic for FlipBox.xaml
/// </summary>
public partial class FlipBox : UserControl
{
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(
"Title", typeof(String), typeof(FlipBox), new PropertyMetadata("nothing"));
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(String), typeof(FlipBox), new PropertyMetadata("nothing"));
public static readonly DependencyProperty BoxModeProperty = DependencyProperty.Register(
"BoxMode", typeof(String), typeof(FlipBox), new PropertyMetadata("Box"));
public FlipBox()
{
InitializeComponent();
this.DataContext = this;
}
public String Title
{
get { return (String)this.GetValue(TitleProperty); }
set { this.SetValue(TitleProperty, value); }
}
public String Text
{
get { return (String)this.GetValue(TextProperty); }
set { this.SetValue(TextProperty, value); }
}
public String BoxMode
{
get { return (String)this.GetValue(BoxModeProperty); }
set { this.SetValue(BoxModeProperty, value); }
}
}
}
Thanks in advance.
You can use triggers to set the template. Replace the StackPanel in your UserControl with this...
<StackPanel Orientation="Horizontal" Height="Auto" Width="Auto" >
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=BoxMode}" Value="Box">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal" Height="Auto" Width="Auto">
<TextBlock Text="{Binding Path=Title}" Height="Auto" Width="Auto" />
<TextBlock Text="{Binding Path=Text}" Height="Auto" Width="Auto"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=BoxMode}" Value="Block">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Title}" Height="Auto" Width="Auto" />
<TextBox Text="{Binding Path=Text}" Height="Auto" Width="Auto" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</StackPanel>
Here is an example of how you can create a conditional control:
public class ConditionalControl : ContentControl
{
static ConditionalControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof (ConditionalControl), new FrameworkPropertyMetadata(typeof (ConditionalControl)));
}
#region Condition DP
public bool Condition
{
get { return (bool) GetValue(ConditionProperty); }
set { SetValue(ConditionProperty, value); }
}
public static readonly DependencyProperty ConditionProperty =
DependencyProperty.Register("Condition", typeof (bool), typeof (ConditionalControl), new UIPropertyMetadata(false));
#endregion
#region TrueTemplate DP
public DataTemplate TrueTemplate
{
get { return (DataTemplate) GetValue(TrueTemplateProperty); }
set { SetValue(TrueTemplateProperty, value); }
}
public static readonly DependencyProperty TrueTemplateProperty =
DependencyProperty.Register("TrueTemplate", typeof (DataTemplate), typeof (ConditionalControl), new UIPropertyMetadata(null));
#endregion
#region FalseTemplate DP
public DataTemplate FalseTemplate
{
get { return (DataTemplate) GetValue(FalseTemplateProperty); }
set { SetValue(FalseTemplateProperty, value); }
}
public static readonly DependencyProperty FalseTemplateProperty =
DependencyProperty.Register("FalseTemplate", typeof (DataTemplate), typeof (ConditionalControl), new UIPropertyMetadata(null));
#endregion
}
Here is it's style that you need to put in Themes/Generic.xaml
in you project:
<Style TargetType="{x:Type Controls:ConditionalControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Controls:ConditionalControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentPresenter x:Name="FalseContentPresenter"
Content="{TemplateBinding DataContext}"
ContentTemplate="{TemplateBinding FalseTemplate}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Condition"
Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Controls:ConditionalControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentPresenter x:Name="TrueContentPresenter"
Content="{TemplateBinding DataContext}"
ContentTemplate="{TemplateBinding TrueTemplate}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
You will likely need an IValueConverter
to change that string
to a ControlTemplate
that is expected. It will not do the lookup of the resource key when passed as a bare string. A different solution would be to use a Style
to alter the visibility:
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}" />
<TextBox Text="{Binding Text}">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding BoxMode}" Value="Box">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBlock Text="{Binding Text}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding BoxMode}" Value="Box">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
精彩评论