Legends Legends - 3 months ago 17
C# Question

Hover over a button and highlight multiple controls when condition is met (MVVM)

I have two buttons with commands bound to them.
If the user hovers over a button1 it should highlight (border color changes)
textbox1 and combobox1 only when the button1 is in disabled state.

If the user hovers over a button2 it should highlight (border color changes)
textbox2, textbo3 and combobox1 only when the button2 is in disabled state.

And finally unhighlight the controls on mouseleave.

Is this possible with pure XAML, because the style should be applied to other controls, not to the button itself who triggers the event and only when conditions are met?
And how could this be done?

I found many examples on SO, but they do not apply to this specific case.

I started to do this programmatically:

<Button Name="btnGenerateHash"
IsEnabled="{Binding VM.IsGenerateButtonEnabled}"
Command="{Binding GenerateCommand}"
Content="{Binding VM.GenerateHashButtonLabel}" Width="160"
Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Margin="10" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding HighlightFieldsCommand}" CommandParameter="{Binding Source='generate,enter'}"></i:InvokeCommandAction>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<i:InvokeCommandAction Command="{Binding HighlightFieldsCommand}" CommandParameter="{Binding Source='generate,leave'}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>


Modified version:

<StackPanel Orientation="Vertical">
<CheckBox
x:Name="EnableButton1CheckBox"
Content="Enable Button1"
Margin="4"
/>

<Grid Margin="4">

<Button Click="button1_Click"
Content="Button1"
x:Name="button1"
IsEnabled="{Binding IsChecked, ElementName=EnableButton1CheckBox}">

</Button>



<Border
x:Name="Button1MouseDetector"
Background="Transparent"
>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Tag" Value="MouseOver" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>


<!--
When button1 is disabled, it can't receive mouse events, so we create a
coextensive control that's explicitly transparent. If it merely had no
background specified, it wouldn't get mouse events either.
-->

</Grid>


<Grid Margin="4">
<Button
Content="Button2"
x:Name="button2"
IsEnabled="{Binding IsChecked, ElementName=EnableButton1CheckBox}">

</Button>



<Border
x:Name="Button2MouseDetector"
Background="Transparent"
>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Tag" Value="MouseOver" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>


<!--
When button1 is disabled, it can't receive mouse events, so we create a
coextensive control that's explicitly transparent. If it merely had no
background specified, it wouldn't get mouse events either.
-->

</Grid>

<TextBox
Text="tb1"
x:Name="tb1"
Margin="4"
>
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition
Binding="{Binding Tag, ElementName=Button1MouseDetector}"
Value="MouseOver"
/>
<Condition
Binding="{Binding IsEnabled, ElementName=button1}"
Value="False"
/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Red" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition
Binding="{Binding Tag, ElementName=Button2MouseDetector}"
Value="MouseOver"
/>
<Condition
Binding="{Binding IsEnabled, ElementName=button2}"
Value="False"
/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="Green" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>

Answer

This is partial, but illustrates how to

  1. Expose mouseover state of a possibly disabled control to XAML style triggers on other controls, and

  2. How those other XAML triggers can do stuff only when two different conditions are both true.

XAML:

<StackPanel Orientation="Vertical">
    <CheckBox 
        x:Name="EnableButton1CheckBox" 
        Content="Enable Button1" 
        Margin="4"
        />
    <Grid
        Margin="4"
        >
        <Button 
            Content="Button1"
            x:Name="button1"
            IsEnabled="{Binding IsChecked, ElementName=EnableButton1CheckBox}"
            >
        </Button>
        <!-- 
        When button1 is disabled, it can't receive mouse events, so we create a 
        coextensive control that's explicitly transparent. If it merely had no  
        background specified, it wouldn't get mouse events either. 
        -->
        <Border
            x:Name="Button1MouseDetector"
            Background="Transparent"
            >
            <Border.Style>
                <Style TargetType="Border">
                    <Style.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Tag" Value="MouseOver" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </Border.Style>
        </Border>
    </Grid>

    <Button 
        Content="Button2"
        x:Name="button2"
        Margin="4"
        >
        <Button.Style>
            <Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
                <Style.Triggers>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition 
                                Binding="{Binding Tag, ElementName=Button1MouseDetector}" 
                                Value="MouseOver" 
                                />
                            <Condition 
                                Binding="{Binding IsEnabled, ElementName=button1}" 
                                Value="False" 
                                />
                        </MultiDataTrigger.Conditions>
                        <Setter Property="BorderBrush" Value="Red" />
                    </MultiDataTrigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
    </Button>
</StackPanel>