0

I am trying to figure out how to get data triggers to work between user controls - either between a window and a child user control (a user control embedded in the window), or between a user control that has a child user control.

The button control has 5 buttons but by default the 5th button is collapsed. When the combobox item "Fifth Button" is selected I want the Fourth button to collapse and the Fifth button to become visible. As you can see I have the triggers set to update the Label on the Mainwindow based on the combobox selection. I have no issue using triggers within the same window but I don't know how to make them work to communicate to a user control that is embedded in the same window. Or from one control to another.

<Window x:Class="ComboboxControlChange.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ComboboxControlChange"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <ComboBox Grid.Row="0" x:Name="ButtonSelectCombobox" SelectedValuePath="Content" SelectedValue="{Binding ButtonSelection}" Height="24" Margin="150,0">
                    <ComboBoxItem x:Name="FirstButtonSelection" >First Button</ComboBoxItem>
                    <ComboBoxItem x:Name="SecondButtonSelection">Second Button</ComboBoxItem>
                    <ComboBoxItem x:Name="ThirdButtonSelection">Third Button</ComboBoxItem>
                    <ComboBoxItem x:Name="FourthButtonSelection">Fourth Button</ComboBoxItem>
                    <ComboBoxItem x:Name="FifthButtonSelection">Fifth Button</ComboBoxItem>
                </ComboBox>
                <StackPanel Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" Orientation="Vertical">
                    <Label>You have selected button:</Label>
                    <Label HorizontalAlignment="Center">
                        <Label.Style>
                            <Style TargetType="{x:Type Label}">
                                <Setter Property="Content" Value=""/>
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding ElementName=FirstButtonSelection, Path=IsSelected}" Value="true">
                                        <Setter Property="Content" Value="One" />
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding ElementName=SecondButtonSelection, Path=IsSelected}" Value="true">
                                        <Setter Property="Content" Value="Two" />
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding ElementName=ThirdButtonSelection, Path=IsSelected}" Value="true">
                                        <Setter Property="Content" Value="Three" />
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding ElementName=FourthButtonSelection, Path=IsSelected}" Value="true">
                                        <Setter Property="Content" Value="Four" />
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding ElementName=FifthButtonSelection, Path=IsSelected}" Value="true">
                                        <Setter Property="Content" Value="Five" />
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </Label.Style>
                    </Label>
                </StackPanel>
            </Grid>            
        </Grid>
        <Grid Grid.Row="1">
            <local:ButtonControl />                            
        </Grid>
    </Grid>
</Window>



<UserControl x:Class="ComboboxControlChange.ButtonControl"
             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:ComboboxControlChange"
             mc:Ignorable="d" 
             d:DesignHeight="160" d:DesignWidth="517">
    <Grid Name="Link1MainGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0" VerticalAlignment="Center" Margin="5,0" >
            <TextBlock TextAlignment="Center">
                First<LineBreak/>Button
            </TextBlock>
        </Button>
        <Button Grid.Column="1" VerticalAlignment="Center" Margin="5,0">
            <TextBlock TextAlignment="Center">
                Second<LineBreak/>Button
            </TextBlock>
        </Button>
        <Button Grid.Column="2" VerticalAlignment="Center" Margin="5,0">
            <TextBlock TextAlignment="Center">
                Third<LineBreak/>Button
            </TextBlock>
        </Button>
        <Button Grid.Column="3" VerticalAlignment="Center" Margin="5,0" >
            <Button.Style>
                <Style TargetType="{x:Type Button}">
                    <Setter Property="Visibility" Value="Visible" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ElementName=FifthButtonSelected, Path=IsSelected}" Value="true">
                            <Setter Property="Visibility" Value="Collapsed"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
            <TextBlock TextAlignment="Center">
                Fourth<LineBreak/>Button
            </TextBlock>
        </Button>
        <Button Grid.Column="3" Margin="4,4,4,50" Visibility="Collapsed">
            <Button.Style>
                <Style TargetType="{x:Type Button}">
                    <Setter Property="Visibility" Value="Collapsed" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ElementName=FifthButtonSelected, Path=IsSelected}" Value="true">
                            <Setter Property="Visibility" Value="Visible"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>            
            <TextBlock TextAlignment="Center">
                Fifth<LineBreak/>Button
            </TextBlock>
        </Button>
    </Grid>
</UserControl>

I've tried binding the buttons with ElementName, Path, and even relativeSource but have't had any success. I've also tried adding the triggers in the ButtonControl.Resources section of the control.

 <DataTrigger Binding="{Binding ElementName=FifthButtonSelected, Path=IsSelected}" Value="true">

<DataTrigger Binding="{Binding RelativeSource={RelatvieSource FindAncestorType={x:Type ComboBoxItem}}, Path=IsSelected}" Value="true">

Any help would be appreciated!

H.B.
  • 166,899
  • 29
  • 327
  • 400
Caleb
  • 23
  • 1
  • 5

1 Answers1

0

This won't work because the element names that are in the window will not be in scope for the user control. MSDN says:

[...] the primary XAML namescope is defined at the XAML root element of a single XAML production, and encompasses the elements that are contained in that XAML production.

What that means, practically, is that when you define an x:Name for an element in a file, it can only be referenced in that file, and will be unknown outside of it.

Another way to go about this would be to create a Dependency Property on the user control, and use that as a way to pass information between the window and the control. A nice side affect is that this creates some abstraction and allows for more flexibility.

ButtonControl.xaml.cs: (Rename 'Feature' to something relevant)

public partial class ButtonControl : UserControl
{
    ...

    public bool IsFeatureVisible
    {
        get { return (bool)GetValue(IsFeatureVisibleProperty); }
        set { SetValue(IsFeatureVisibleProperty, value); }
    }

    // Using a DependencyProperty as the backing store for IsFeatureVisible.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsFeatureVisibleProperty =
        DependencyProperty.Register("IsFeatureVisible", typeof(bool), typeof(ButtonControl), new UIPropertyMetadata(false));

    ...
}

Now you could wire up the trigger to use this property, but we're lucky in this case that you're dealing with booleans and Visibility, so we can make it simpler:

ButtonControl.xaml:

<UserControl x:Class="ComboboxControlChange.ButtonControl"
            x:Name="Myself"
            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:ComboboxControlChange"
            mc:Ignorable="d" 
            d:DesignHeight="160" d:DesignWidth="517">

    <UserControl.Resources>
        <BooleanToVisibilityConverter x:Key="mBooleanToVisibilityConverter"/>
    </UserControl.Resources>

    <Grid Name="Link1MainGrid">

    ...

        <Button Grid.Column="3" VerticalAlignment="Center" Margin="5,0"  Visibility="{Binding ElementName=Myself, Path=IsFeatureVisible, Converter={StaticResource mBooleanToVisibilityConverter}}">
            <TextBlock TextAlignment="Center">
                Fourth<LineBreak/>Button
            </TextBlock>
        </Button>

        ...
    </Grid>
</UserControl>

Lastly, in the Window, we need to provide a value for that property on the instance of our button control. We can use the FifthButtonSelection element name here just like you are in other parts of the file:

MainWindow.xaml

<local:ButtonControl IsFeatureVisible="{Binding ElementName=FifthButtonSelection, Path=IsSelected}"/>
plast1k
  • 793
  • 8
  • 15
  • In the DependencyControl dpmt – Caleb Jul 18 '16 at 13:57
  • In the IsFeatureVisible getter, shouldn't you be casting to a bool instead of a string? I made that change and it's still not working. In the ButtonControl you didn't have the data triggers listed - are they still supposed to be added like before with the addition of the "Visibility" binding to the Myself element and Path to IsFeatureVisible? When I breakpointed the Dependency Property getter and setting nothing is happening so I'm feeling I didn't set it up properly but I made the changes per your code above. The UserControl.Resource mBooleanToVisibilityConverter is listed but is it not used? – Caleb Jul 18 '16 at 14:00
  • Oops, you're correct it should've been a bool, and correct that I forgot to specify the converter - I've edited the binding to include it. Also this method replaces the need for the triggers that set the buttons visibility. – plast1k Jul 18 '16 at 14:05
  • Awesome, that works! So what about if I wanted to have the 4th button display at all times (for combobox items 1-4) then if combobox item 5 is selected it would collapse the 4th button and make the 5th button visible? This is simple with triggers but due to the control in the window it gets a bit trickier. – Caleb Jul 18 '16 at 14:52
  • There are a few ways to do this - if the case is as specific as you mentioned you could do it the same way as we just did, but with an inverse boolean converter, see http://stackoverflow.com/questions/534575/how-do-i-invert-booleantovisibilityconverter. – plast1k Jul 18 '16 at 15:56
  • I just got that last scenario working, thanks again. – Caleb Jul 18 '16 at 17:10