Search code examples
c#wpfxamlwpf-controls

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="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.


Solution

  • 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>