xamluser-controlsmultidatatrigger

xaml usercontrol multidatatrigger from parent control and itself (usercontrol)


So I'm trying to learn how to dynamically apply style changes to controls. I have not been able to get a user control to change its borderbrush and background based off a radio button in the main window and the usercontrol's text property. Basing it just off the usercontrol's text property does seem to work. So it appears that I'm doing something wrong with getting the radio button's isCheck property.

I've simplified from the original code but this still shows the issue.

MainWindow.xaml

    <Window x:Class="UserControlTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:UserControlTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <RadioButton x:Name="calcPace" TabIndex="1" Content="Pace" HorizontalAlignment="Left" Margin="34,50,0,0" VerticalAlignment="Top" GroupName="CalculationType"
                     Height="16" Width="41"/>
        <RadioButton x:Name="calcDistance" TabIndex="2" Content="Distance" HorizontalAlignment="Left" Margin="80,50,0,0" VerticalAlignment="Top" GroupName="CalculationType"
                     Height="16" Width="61"/>
        <RadioButton x:Name="calcTime" TabIndex="3" Content="Time" HorizontalAlignment="Left" Margin="146,50,0,0" VerticalAlignment="Top" GroupName="CalculationType"
                     Height="16" Width="42"/>
        <local:TextBoxTime/>
    </Grid>
</Window>

TextBoxTime.xaml (usercontrol):

<UserControl x:Class="UserControlTest.TextBoxTime"
         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:local="clr-namespace:UserControlTest"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <TextBox x:Name="timeString" TabIndex="4" HorizontalAlignment="Left" Height="23" Margin="68,130,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
        <TextBox.Style>
            <Style TargetType="TextBox">
                <Setter Property="BorderBrush" Value="PaleGreen"/>
                <Setter Property="Background" Value="White"/>
                <Style.Triggers>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding ElementName=calcTime, Path=IsChecked}" Value="False"/>
                            <Condition Binding="{Binding ElementName=timeString, Path=Text}" Value=""/>
                        </MultiDataTrigger.Conditions>
                        <Setter Property="BorderBrush" Value="Red"/>
                        <Setter Property="Background" Value="Snow"/>
                    </MultiDataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>
</Grid>

Currently I've added no code behind for either.

Thanks


Solution

  • Here's how I might do it:

    public partial class RequireableTextBox : UserControl
    {
        public RequireableTextBox()
        {
            InitializeComponent();
        }
    
        #region IsRequired Property
        public bool IsRequired
        {
            get { return (bool)GetValue(IsRequiredProperty); }
            set { SetValue(IsRequiredProperty, value); }
        }
    
        public static readonly DependencyProperty IsRequiredProperty =
            DependencyProperty.Register(nameof(IsRequired), typeof(bool), typeof(RequireableTextBox),
                new PropertyMetadata(false));
        #endregion IsRequired Property
    
        #region Text Property
        public String Text
        {
            get { return (String)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
    
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register(nameof(Text), typeof(String), typeof(RequireableTextBox),
                //  Default must be "" not null, for the trigger to understand
                new PropertyMetadata(""));
        #endregion Text Property
    }
    

    XAML

    <UserControl 
        x:Class="UserControlTest.RequireableTextBox"
        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:local="clr-namespace:UserControlTest"
        mc:Ignorable="d" 
        d:DesignHeight="300" d:DesignWidth="300"
        IsTabStop="False"
        >
        <Grid>
            <TextBox 
                x:Name="timeString" 
                HorizontalAlignment="Left" 
                TextWrapping="Wrap" 
                VerticalAlignment="Top" 
                Width="120"
                Text="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}, UpdateSourceTrigger=PropertyChanged}"
                >
                <TextBox.Style>
                    <Style TargetType="TextBox">
                        <Setter Property="BorderBrush" Value="PaleGreen"/>
                        <Setter Property="Background" Value="White"/>
    
                        <Style.Triggers>
                            <!-- 
                            Seemed right to disable when unneeded; delete this trigger 
                            if you'd rather not.
                            -->
                            <DataTrigger 
                                Binding="{Binding IsRequired, RelativeSource={RelativeSource AncestorType=UserControl}}"
                                Value="False"
                                >
                                <Setter Property="IsEnabled" Value="False" />
                            </DataTrigger>
    
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition 
                                        Binding="{Binding IsRequired, RelativeSource={RelativeSource AncestorType=UserControl}}" 
                                        Value="True"
                                        />
                                    <Condition 
                                        Binding="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}}" 
                                        Value=""
                                        />
                                </MultiDataTrigger.Conditions>
                                <Setter Property="BorderBrush" Value="Red"/>
                                <Setter Property="Background" Value="Snow"/>
                            </MultiDataTrigger>
                        </Style.Triggers>
                    </Style>
                </TextBox.Style>
            </TextBox>
        </Grid>
    </UserControl>
    

    Usage:

    <StackPanel>
        <RadioButton x:Name="calcTime" GroupName="CalculationType">Calculate Time</RadioButton>
        <RadioButton x:Name="calcDistance" GroupName="CalculationType">Calculate Distance</RadioButton>
        <local:RequireableTextBox
            IsRequired="{Binding IsChecked, ElementName=calcTime}"
            />
        <local:RequireableTextBox
            x:Name="DistanceValue"
            IsRequired="{Binding IsChecked, ElementName=calcDistance}"
            />
        <!-- Just tossed this in to demonstrate the Text property -->
        <Label Content="{Binding Text, ElementName=DistanceValue}" Foreground="Gray" />
    </StackPanel>