How to prevent children from triggering a mouse over event? - wpf

I have a TabItem with a Grid and CheckBox children.
<TabControl>
<TabItem Header="First" Style="{DynamicResource CustomTabItemStyle}">
<Grid>
<CheckBox Content="Check Me"/>
<CheckBox Content="Check Me 2"/>
</Grid>
</TabItem>
</TabControl>
I'm styling the TabItem and setting the IsMouseOver trigger to change the TabItem background color like this:
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="mainBorder" Value="{StaticResource TabItem.MouseOver.Background}"/>
<Setter Property="Opacity" TargetName="mainBorder" Value="0.1"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{StaticResource TabItem.MouseOver.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="0"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="0"/>
</MultiDataTrigger>
The problem is that when the mouse is over the checkbox the background color change triggers. It happens with every child element under TabItem
I tried setting a Tag="TabItemParent" to the TabItem, but it didn't work. I tried many other conditions together such as IsMouseDirectlyOver, but I can't get it to work only when the mouse is over the actual TabItem and not any of its child elements. Also tried with C# MouseEnter and MouseLeave, it does the same.
Looked online too and couldn't find anything on the topic.
Ideally it would be best to have a fully XAML solution.

Changed this line from the style:
<Condition Binding="{Binding IsMouseOver, ElementName=templateRoot}" Value="true"/>
It seems that RelativeSource={RelativeSource Self}} will target all children and the parent.

Related

How to show always the blue color of a selected row, no matter if it has the focus or not?

I am trying to show always the blue color of the row of a DataGrid, no matter if it has the focus or not. I am trying this code:
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=IsSelected}" Value="true"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="{x:Static SystemColors.HighlightBrushKey}" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
But I get the error that SystemResourcesKey is not valid, it is only possible to use BindingBase elements.
How can I set the default blue color of the row when the row is selected?
There are a few reasons why your current approach does not work:
You refer to the Key of the property, not the brush HighlightBrush
You have to bind the IsSelected property on the DataGridRow itself using RelativeSource
The row background is occluded by each cell that has background on its own
In order to make it work, replace the key by the brush, adapt the binding to IsSelected and add a cell style that sets the background of the individual cells to the desired background. Alternatively, you could set the cell background to Transparent, so the row background is not occluded.
Please note, that I also added setters to adapt the Foreground and BorderBrush, too, so that it looks the same as in selected state. However, I recommend you to not use the BorderBrush, since the border color is also used in validation and other visual states.
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Value="True" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="{x:Static SystemColors.HighlightBrush}" />
<Setter Property="Foreground" Value="{x:Static SystemColors.HighlightTextBrush}" />
<Setter Property="BorderBrush" Value="{x:Static SystemColors.HighlightBrush}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="{x:Static SystemColors.HighlightBrush}" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
If you do not care about the background of the cells that do not belong to any column (that take up the remaining space to the right) you do not need the row style at all.
To fix the error with SystemResourceKey:
<Setter Property="Background" Value="{StaticResource {x:Static SystemColors.HighlightBrushKey}}" />

MultiDataTrigger with AlternationIndex

I have a ListView declared as:
<ListView x:Name="Tree"
ItemsSource="{Binding ElementName=This, Path=Some.Path.Values}"
AlternationCount="2"
ScrollViewer.CanContentScroll="False">
and a style defined as
<UserControl.Resources>
<Style TargetType="ListViewItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="SteelBlue"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Gray"/>
</Style.Resources>
<Style.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="White" />
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="GhostWhite" />
</Trigger>
</Style.Triggers>
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<EventSetter Event="Loaded" Handler="ContinueLoading" />
</Style>
This combination produced the original desired behaviour, which is that of alternating background highlights. The new desired behaviour was to change that background color depending on the value of a property of a given ListView item; as such the Style.Triggers was changed to
<Style.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="White" />
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="GhostWhite" />
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="0"/>
<Condition Binding="{Binding Converter={x:Static controls:Converters.ObjectType}}" Value="{x:Type client:DocumentEntryTypeA}" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="{Binding Converter={x:Static controls:Converters.LightColor}, UpdateSourceTrigger=PropertyChanged, Path=Status}" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="1"/>
<Condition Binding="{Binding Converter={x:Static controls:Converters.ObjectType}}" Value="{x:Type client:DocumentEntryTypeA}" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="{Binding Converter={x:Static controls:Converters.DarkColor}, UpdateSourceTrigger=PropertyChanged, Path=Status}" />
</MultiDataTrigger>
</Style.Triggers>
</UserControl.Resources>
The ObjectType Converter checks that an element is of a given class; the LightColor and DarkColor Converters produce the selected background values depending on the value of the Status property.
The issue with this code is that the binding I use seems to always produce an AlternationIndex value of '0', i.e. the Converter LightColor is used for every entry. In addition to the code above, I have also tried the following bindings with the same result:
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ItemsControl}, Path=AlternationIndex}" Value="0"/>
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListView}}, Path=(ItemsControl.AlternationIndex)}" Value="0"/>
Based on the examples I've seen most of the solutions don't separate the style from the object; in my case the style is defined separately within UserControl.Resources. However, since using a Trigger works fine, I'm not sure why a DataTrigger does not, or what would be required to get it working.
The first condition in your MultiDataTrigger finds the most recent ContentPresenter, and tries to bind to ContentPresenter.ItemsControl.AlternationIndex, and ItemsControl.AlternationIndex is not a valid property for ContentPresenter.
Try changing that to RelativeSource={RelativeSource Self} so you will be binding to the ItemsControl.AlternationIndex of the current object

WPF DataTrigger on ViewModel Property Change

I'm trying to fire a trigger when a property in my ViewModel changes. I can't seem to get the trigger to fire no matter what I try. My XAML looks like this:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="Padding" Value="0 1" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpanding}" Value="True">
<Setter Property="Control.Template" Value="{StaticResource ResourceKey=loadingTreeViewItem}" />
</DataTrigger >
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
The IsExpanded and IsSelected bindings are working fine, however I can't get the DataTrigger to fire when IsExpanding is true. These properties are in the same ViewModel. I have tried adding different variations for RelativeSource but am not having any luck. Any feedback is appreciated.
Turns out the trigger was working. The problem was that the IsExpanding property and the call to get the data for the TreeView were both happening on the UI thread. I threaded the call to get the data and everything is working as expected

How to control visibility of text in all listboxitems on selection of one item in dynamic Listbox Menu?

I generate ListBox menu from XML. I use datatemplate to style the behavior of listboxitems on selection and other states. I need to hide all textblocks in all listboxitems on selection of the item which gets a value ‘retract’ from XML. Now, I am able to hide texblock only in listboxitem which has this value but cannot hide textblocks in other listboxitems. I am wondering if someone can help. Thank you in advance.
<DataTemplate x:Key="ListBoxItemDataTemplate">
<Grid x:Name="DataItem">
<Image x:Name="IconImage" Source="{Binding XPath=#icon}" Height="16" Margin="16,0,0,0" Stretch="None" VerticalAlignment="Center" HorizontalAlignment="Left" />
<TextBlock x:Name="ListboxIemtextBlock" Text="{Binding XPath=#name}" />
<Image x:Name="ArrowImage" Height="10" Source="Resources/Images/arrow_collapsed_grey.png" Visibility="{Binding XPath=#state}"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True">
<Setter TargetName="ListboxIemtextBlock" Property="Foreground" Value="White"/>
<Setter TargetName="IconImage" Property="Source" Value="{Binding XPath=#iconSelected}"/>
<Setter TargetName="IconImage" Property="Height" Value="16"/>
<Setter TargetName="ArrowImage" Property="Source" Value="Resources/Images/arrow_collapsed_white.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True">
<Setter TargetName="ListboxIemtextBlock" Property="Foreground" Value="#FF6dacbe"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True" />
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter TargetName="ListboxIemtextBlock" Property="Foreground" Value="White"/>
</MultiDataTrigger>
<DataTrigger Binding="{Binding XPath=#retract}" Value="True" >
<Setter TargetName="ListboxIemtextBlock" Property="Visibility" Value="Hidden"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
It looks like I cannot control the visibility of all texblocks from a datatemplate. I think it should be done in the ListBox style. I was thinking to switch datatemplates with a second datatemplate that does not have texblock at all. I wanted to use multitrigger for conditions with values isSelected and XML-Binding to Binding="{XPath=#retract}. However, I cannot assign XPath binding for multitrigger in Listbox style. Perphaps, you might help to bind it correctly or have a better idea on how to hide texblocks.
<Style x:Key="ListBoxItemContainerStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="ContentTemplate" Value="{StaticResource ListBoxItemDataTemplate}"/>
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter x:Name="contentPresenter"/>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Binding="{XPath=#retract}" Value="true"/>
</MultiTrigger.Conditions>
<Setter Property="ContentTemplate" Value="{StaticResource SelectedListBoxItemDataTemplate}"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I populated XML with XMLDataProvider. I reference to xml this way:
<XmlDataProvider x:Key="PagesData" XPath="/Pages" Source="Data/DataSource.xml" />
XML:
<Pages xmlns="">
<page name="Item 1" icon="Resources/Iocn1.png" retract="False" />
<page name="Item 2" icon="Resources/Iocn2.png" retract="False" />
<page name="Item 3" icon="Resources/Iocn3.png" retract="True" /></Pages>
You can bind to the SelectedItem.retract for the Parent ListBox. This is a working example using Path instead of XPath (since I don't have your XML source) but you should be able to get it to work the same way
Add this trigger in the DataTemplate
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}, Path=SelectedItem.retract}" Value="True" >
<Setter TargetName="ListboxIemtextBlock" Property="Visibility" Value="Hidden"/>
</DataTrigger>

Condition Binding Attribute Not Working?

I've been struggling with this code for some time now and can't seem to find any complete answers to my question. I've created a small sample to illustrate the problem:
<ListView >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="0,0,20,0" IsItemsHost="True" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Items>
<TextBlock>Test1</TextBlock>
<TextBlock>Test2</TextBlock>
<TextBlock>Test3</TextBlock>
<TextBlock>Test4</TextBlock>
<TextBlock>Test5</TextBlock>
</ListView.Items>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid>
<ContentPresenter/>
</Grid>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True" />
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Visibility" Value="Collapsed"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
According to the MultiTrigger settings, the selected item should reappear when the mouse is no longer over the selected item. This code, however, produces an InvalidOperationException with the message "Must have non-null value for 'Property'." If you remove the Condition that uses the "Binding" attribute the exception is not thrown. In the MSDN documentation it states that you must have either the Property or Binding attribute set. The above code functions like the Binding attribute is not set. In fact, in all my test cases, it doesn't matter what the Binding attribute is set to; the exception is still thrown. Any thoughts?
This is one of those times when you have to suck it up and admit that you've made a bonehead mistake. However, to save some other unlucky soul from the same fate, I'll reveal my epiphany.
First, if I had read all of the documentation I would have read the part that said if you're using the condition's "Binding" attribute, it needs to be included in a MultiDataTrigger element (instead of the MutiTrigger element in my posted example).
Second, upon making those changes, the MultiTrigger element is replace with the following code:
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True" />
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsSelected}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Collapsed"/>
</MultiDataTrigger>
Now the example works but because the selected item is collapsed, the trigger condition switches back and forth causing the selected item to flicker in and out of view. Makes sense but admittedly not what I intended.
At any rate, hope this helps someone from making the same bonehead mistake!
On a very similar note, pulling IsMouseOver from a border as the main data template content, and pulling the IsSelected from the Ancestor. Its interesting that both conditions have to have a relative path, I would assume that the default path would be the local datacontext. Thanks for the above solution.
Broken Code
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
Value="True" />
<Condition SourceName="Border"
Property="IsMouseOver"
Value="True" />
</MultiDataTrigger.Conditions>
<Setter TargetName="Border"
Property="Background"
Value="{StaticResource OnBrushSelected}" />
</MultiDataTrigger>
Working Code
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}, Path=IsMouseOver}"
Value="True" />
<Condition Binding="{Binding Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
Value="True" />
</MultiDataTrigger.Conditions>
<Setter TargetName="Border"
Property="Background"
Value="{StaticResource OnBrushSelected}" />
</MultiDataTrigger>

Resources