CEDA
CEDA

Reputation: 55

Conditional blocks in XAML

I would like to write a conditional control as below:

<local:ConditionalBlock Condition={Binding CertainBoolValue}>
    <ConditionalBlock.Match>
        <!-- Any content here -->
    </ConditionalBlock.Match>
    <ConditionalBlock.Else>
        <!-- Any content here -->
    </ConditionalBlock.Else>
</local:ConditionalBlock>

At the moment I don't know how this should be implemented. So please help. Thanks

EDITING

While waiting for the response I have implemented my own solution using Custom Control & control template as following:

using System.Windows;
using System.Windows.Controls;

namespace Sample
{
    public class ConditionalControl : ContentControl
    {
        static ConditionalControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ConditionalControl), new FrameworkPropertyMetadata(typeof(ConditionalControl)));
        }

        public object Alternative
        {
            get { return (object)GetValue(AlternativeProperty); }
            set { SetValue(AlternativeProperty, value); }
        }

        public static readonly DependencyProperty AlternativeProperty =
            DependencyProperty.Register("Alternative", typeof(object), typeof(ConditionalControl), new UIPropertyMetadata(null));

        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(null));
    }
}

ConditionalControl.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Sample">

    <Style TargetType="{x:Type local:ConditionalControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ConditionalControl}">
                    <Grid>
                        <ContentPresenter Name="match"/>
                        <ContentPresenter Name="alternative" ContentSource="Alternative" />
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="Condition" Value="True">
                            <Setter TargetName="match" Property="Visibility" Value="Visible"/>
                            <Setter TargetName="alternative" Property="Visibility" Value="Collapsed"/>
                        </Trigger>
                        <Trigger Property="Condition" Value="False">
                            <Setter TargetName="match" Property="Visibility" Value="Collapsed"/>
                            <Setter TargetName="alternative" Property="Visibility" Value="Visible"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Sample usage:

<Grid>
    <local:ConditionalControl Condition="{Binding CertainBoolValue}">
        <Label>Match case</Label>
        <local:ConditionalControl.Alternative>
            <Label>Alternative case</Label>
        </local:ConditionalControl.Alternative>
    </local:ConditionalControl>
</Grid>

Anyway, thank Luke Woodward for your prompt response. I'll mark your answer as accepted

Upvotes: 0

Views: 1666

Answers (1)

Luke Woodward
Luke Woodward

Reputation: 64959

One way to do this is to create a UserControl with the following XAML:

<ContentControl x:Class="WpfApplication1.ConditionalBlock"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" />

and the following code-behind:

using System.Windows;
using System.Windows.Controls;

namespace WpfApplication1
{
    public partial class ConditionalBlock : ContentControl
    {
        public static readonly DependencyProperty ConditionProperty =
            DependencyProperty.Register("Condition", typeof(bool), typeof(ConditionalBlock),
                                        new FrameworkPropertyMetadata(Condition_Changed));

        public ConditionalBlock()
        {
            InitializeComponent();
            Loaded += ((s, e) => UpdateContent());
        }

        public bool Condition
        {
            get { return (bool)GetValue(ConditionProperty); }
            set { SetValue(ConditionProperty, value); }
        }

        public object Match { get; set; }

        public object Else { get; set; }

        private static void Condition_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var conditionalBlock = obj as ConditionalBlock;
            if (conditionalBlock != null)
            {
                conditionalBlock.UpdateContent();
            }
        }

        private void UpdateContent()
        {
            Content = (Condition) ? Match : Else;
        }
    }
}

Note that we've changed the superclass from UserControl to ContentControl.

This uses a dependency property for the condition, so that we get notified when the bound value changes. When the condition changes, we update which content is visible by assigning to the Content dependency property we inherit from ContentControl. Finally, to ensure that one or other content appears when the control is first shown, we update the content in a handler for the Loaded event. (Calling it from within the constructor doesn't work, as at that point the Match and Else properties haven't been set.)

I've assumed that the content of the Match and Else properties won't change, so I haven't bothered to implement them as dependency properties.

Upvotes: 2

Related Questions