Setting ContentPresenter with MultiDataTrigger produces memory leak - wpf

I have a ListBox which Item's ContentPresenter is changed by MultiDataTrigger depending on some IsTool and IsAlerting boolean properties:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="LayerTemplate">
<ContentControl x:Name="contentControl" Style="{DynamicResource PageHeaderContentControlStyle}">
<layers:PageHeader/>
</ContentControl>
</DataTemplate>
<DataTemplate x:Key="ToolTemplate">
<ContentControl x:Name="contentControl" Style="{DynamicResource ToolHeaderContentControlStyle}">
<layers:ToolHeader/>
</ContentControl>
</DataTemplate>
<DataTemplate x:Key="LayerAlertTemplate">
<ContentControl x:Name="contentControl" Style="{DynamicResource PageHeaderAlertContentControlStyle}">
<layers:PageHeader/>
</ContentControl>
</DataTemplate>
<DataTemplate x:Key="ToolAlertTemplate">
<ContentControl x:Name="contentControl" Style="{DynamicResource ToolHeaderAlertContentControlStyle}">
<layers:ToolHeader/>
</ContentControl>
</DataTemplate>
<Style TargetType="{x:Type ListBoxItem}" x:Key="EmptyListViewSelection">
<Setter Property="Background" Value="Transparent" />
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border BorderBrush="Transparent"
BorderThickness="0"
Background="{TemplateBinding Background}"
Margin="2"
FocusVisualStyle="{x:Null}">
<ContentPresenter DataContext="{Binding}" Name="contentPresenter"/>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsTool}" Value="False">
<Setter TargetName="contentPresenter" Property="ContentTemplate" Value="{StaticResource LayerTemplate}"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsTool}" Value="True">
<Setter TargetName="contentPresenter" Property="ContentTemplate" Value="{StaticResource ToolTemplate}"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsTool}" Value="False"/>
<Condition Binding="{Binding IsAlerting}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter TargetName="contentPresenter" Property="ContentTemplate" Value="{StaticResource LayerAlertTemplate}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsTool}" Value="True"/>
<Condition Binding="{Binding IsAlerting}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter TargetName="contentPresenter" Property="ContentTemplate" Value="{StaticResource ToolAlertTemplate}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox Name="listView"
BorderThickness="0"
ItemsSource="{Binding Pages}"
SelectedValue="{Binding SelectedPage}"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ItemContainerStyle="{StaticResource EmptyListViewSelection}"
IsTabStop="False"
FocusVisualStyle="{x:Null}"
Focusable="False" Background="{x:Null}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" MaxWidth="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=ActualWidth}" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
<GroupBox Name="pageBox" IsEnabled="{Binding Visible}" Grid.Row="1" DataContext="{Binding Path=SelectedValue, ElementName=listView}">
<GroupBox.Header>
<Label Content="{Binding Name}" Padding="0"/>
</GroupBox.Header>
<ContentControl Content="{Binding}"/>
</GroupBox>
</Grid>
If the notification of IsAlerting property change (via INotifyPropertyChange) is fired approximately one time per second, the memory usage of my application increases from 230MB up to 804MB for 15 minutes and the application finally crashes with the following call stack:
Exception Info: System.Reflection.TargetInvocationException
Stack:
at System.Windows.FrameworkTemplate.LoadTemplateXaml(System.Xaml.XamlReader, System.Xaml.XamlObjectWriter)
at System.Windows.FrameworkTemplate.LoadTemplateXaml(System.Xaml.XamlObjectWriter)
at System.Windows.FrameworkTemplate.LoadOptimizedTemplateContent(System.Windows.DependencyObject, System.Windows.Markup.IComponentConnector, System.Windows.Markup.IStyleConnector, System.Collections.Generic.List`1<System.Windows.DependencyObject>, System.Windows.UncommonField`1<System.Collections.Hashtable>)
at System.Windows.FrameworkTemplate.LoadContent(System.Windows.DependencyObject, System.Collections.Generic.List`1<System.Windows.DependencyObject>)
at System.Windows.StyleHelper.ApplyTemplateContent(System.Windows.UncommonField`1<System.Collections.Specialized.HybridDictionary[]>, System.Windows.DependencyObject, System.Windows.FrameworkElementFactory, Int32, System.Collections.Specialized.HybridDictionary, System.Windows.FrameworkTemplate)
at System.Windows.FrameworkTemplate.ApplyTemplateContent(System.Windows.UncommonField`1<System.Collections.Specialized.HybridDictionary[]>, System.Windows.FrameworkElement)
at System.Windows.FrameworkElement.ApplyTemplate()
at System.Windows.FrameworkElement.MeasureCore(System.Windows.Size)
at System.Windows.UIElement.Measure(System.Windows.Size)
at System.Windows.ContextLayoutManager.UpdateLayout()
at System.Windows.ContextLayoutManager.UpdateLayoutCallback(System.Object)
at System.Windows.Media.MediaContext+InvokeOnRenderCallback.DoWork()
at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
at System.Windows.Media.MediaContext.RenderMessageHandlerCore(System.Object)
at System.Windows.Media.MediaContext.RenderMessageHandler(System.Object)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)
.............
any ideas?

Not sure about the memory leak, but i wouldn't change the contenttemplate like this. If you set the ContentTemplate on a ContentPresenter, the ContentSource property of the ContentPresenter (which is 'Content' by default) will be overruled/ignored, the Content property will be empty, and your ContentTemplate doesn't have a datacontext anymore.
It's better to set the ContentTemplate in a style setter and use style triggers to change it. The default ContentSource of the ContentPresenter will do the rest.
I suspect this might solve your memory leak.

Your memory leak may be caused by the situation described in Can bindings create memory leaks in WPF?, perhaps exacerbated by virtualization in the list box (or maybe lack thereof if you have a lot of items and it is allocating them all at once). The fact that you have a System.Reflection.TargetInvocationException makes me think you are binding to something that isn't an INotifyPropertyChanged, which then has to use reflection to figure out if values are changing.
In newer versions of Visual Studio (2013, maybe 2012), there is a memory profiler that can show what objects are leaking which may help you narrow your search.

for some reason moving MultiDataTrigger to a separate style solved the problem (there is no memory leak anymore):
<Style TargetType="{x:Type ContentControl}" x:Key="PageToolHeaderContentControlThemeStyle">
<Setter Property="Foreground" Value="{StaticResource ForegroundBrush}"/>
<Setter Property="Padding" Value="5,3"/>
....
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Alert}" Value="Warning"/>
<Condition Binding="{Binding IsAcknowledged}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter TargetName="PART_BorderAlarmFrame" Property="Stroke" Value="Orange"/>
<Setter TargetName="PART_BorderAlarmFrame" Property="Visibility" Value="Visible"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Alert}" Value="Warning"/>
<Condition Binding="{Binding IsAcknowledged}" Value="False"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter TargetName="PART_BorderAlarmFrame" Property="Visibility" Value="Visible"/>
</MultiDataTrigger.Setters>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="BackgroundBrush"
Storyboard.TargetProperty="Color"
From="Transparent" To="Orange" Duration="0:0:0.5"
AutoReverse="True"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Alert}" Value="Alarm"/>
<Condition Binding="{Binding IsAcknowledged}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter TargetName="PART_BorderAlarmFrame" Property="Stroke" Value="Red"/>
<Setter TargetName="PART_BorderAlarmFrame" Property="Visibility" Value="Visible"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Alert}" Value="Alarm"/>
<Condition Binding="{Binding IsAcknowledged}" Value="False"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter TargetName="PART_BorderAlarmFrame" Property="Visibility" Value="Visible"/>
</MultiDataTrigger.Setters>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="BackgroundBrush"
Storyboard.TargetProperty="Color"
From="Transparent" To="Red" Duration="0:0:0.5"
AutoReverse="True"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
</MultiDataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{DynamicResource LightForegroundBrush}"/>
<Setter TargetName="PART_BorderDefaultFrame" Property="Stroke" Value="{StaticResource ButtonPressedBackgroundBrush}"/>
<Setter TargetName="PART_Border" Property="Background" Value="{StaticResource ButtonHoverBackgroundBrush}"/>
</Trigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Foreground" Value="{DynamicResource LightForegroundBrush}"/>
<Setter TargetName="PART_BorderDefaultFrame" Property="Stroke" Value="{StaticResource ButtonPressedBackgroundBrush}"/>
<Setter TargetName="PART_Border" Property="Background" Value="{StaticResource ButtonPressedBackgroundBrush}"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Related

MultiDataTrigger conditions null check

I am trying to setup a grid's visibility using a multidatatrigger. The condition for making it visible is PropertyA==null && PropertyB==false. I am not sure how PropertyA condition is to be written. My attempt is here (but not working):
<Grid>
<Grid.Style>
<Style>
<Setter Property="Control.Visibility" Value="Collapsed"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding PropertyA}" Value="{x:Null}"/>
<Condition Binding="{Binding PropertyB}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Control.Visibility" Value="Visible" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
Could someone advise pls.
This is the style that I have used, it is for Button but it works as expected
<Style TargetType="Button" x:Key="btnTakePhoto">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Comment}" Value="{x:Null}"/>
<Condition Binding="{Binding ResponseNumber}" Value="{x:Null}"/>
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Collapsed"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
Try to take the Style out of your Grid and include it in your UserControl.Resources tag.
You would use it like so:
<Button Style="{StaticResource btnTakePhoto}"/>
There is absolutely no reason for your xaml not to work.
EDIT
Also I have this code in my other UserControl:
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.Style>
<Style>
<Setter Property="Grid.Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=btnShowHide, Path=IsChecked}" Value="True">
<Setter Property="Grid.Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
<ToggleButton MinHeight="150" Width="50" Content=">" Name="btnShowHide">

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

How to bind to Selector.IsSelectionActive from a DataTemplate?

I have the following style for a ListBoxItem defined:
<Style x:Key="detailListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="AutomationProperties.AutomationId" Value="{Binding Path=StringTableKey}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="2,0,0,0"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" Margin="4" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource MenuItemSelectedBackgroundBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<Trigger Property="IsSelected" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource MenuItemUnSelectedBackgroundBrush}"/>
</Trigger>
<!-- This is the case when a detail is selected (the master list loses focus). -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<!--<Setter Property="Opacity" TargetName="Bd" Value=".4"/>-->
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The ListBox for which this style is applied is defined as:
<ListBox
x:Name="listBox"
Margin="0,60,0,0"
MaxHeight="600"
Foreground="Transparent"
Style="{StaticResource detailListBoxStyle}"
ItemContainerStyle="{StaticResource detailListBoxItemStyle}"
ItemsSource="{Binding Source={StaticResource detailCollectionViewSource}}"
ItemTemplateSelector="{StaticResource detailDataTemplateSelector}"
TouchDown="ListBoxTouchDown"
TouchMove="ListBoxTouchMove"
TouchUp="ListBoxTouchUp"
KeyDown="ListBoxKeyDown">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Margin="0,10,0,0" FontWeight="Bold" FontSize="20" Foreground="White" Text="{Binding Path=Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
</ListBox>
I have a DataTemplate for a ListBoxItem that can be:
<DataTemplate x:Key="detailOnOffTemplate">
<Grid Height="50" Width="{StaticResource detailWidth}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock x:Name="tb1" Margin="4,2,0,0" Grid.Row="0" Style="{StaticResource MenuTextStyle}" Text="{Binding DisplayName}" VerticalAlignment="Top" TextAlignment="Left">
<TextBlock.Effect>
<DropShadowEffect Color="White" ShadowDepth="0" BlurRadius="7"/>
</TextBlock.Effect>
</TextBlock>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True">
<Setter TargetName="tb1" Property="Foreground" Value="White"/>
<Setter TargetName="tb1" Property="Effect" Value="{x:Null}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
I need to be able to bind to "Selector.IsSelectionActive" from within my DataTemplate but nothing works. I've tried these things:
<DataTrigger Binding="{Binding Selector.IsSelectionActive, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True">
<Trigger Binding="{Binding Selector.IsSelectionActive, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}, Mode=FindAncestor}}" Value="True">
<Trigger "Selector.IsSelectionActive" Value="True">
Basically, I want the same trigger that is contained in the ControlTemplate in my DataTemplate:
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<!--<Setter Property="Opacity" TargetName="Bd" Value=".4"/>-->
</MultiTrigger>
Or, how else can I know the item "IsSelected" but does not have keyboard focus?
The first option you tried would be correct exception you're not identifying that that is an attached property so it looks like you are trying to bind to a property named Selector that returns an object that has a property names IsSelectionActive. So I would think something like the following would work:
<DataTrigger Binding="{Binding Path=(Selector.IsSelectionActive), RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" Value="True">
The previous answer didn't work for me, but after many hours of playing this did:
The key here (I think) was that the IsSelectionActive FALSE comes first. And that trigger is also a multitrigger combined with the IsSelected. I didn't have to use brackets in this case. But that might be because I'm using the latest .net framework, Wpf and Visual Studio, so you may need the brackets spoken of in the previous answer.
The red, yellow and white is just for this sample, remember to change those colors.
<ControlTemplate.Triggers>
<MultiTrigger>
<!-- selected, but not focused -->
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False" />
<Condition Property="IsSelected" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="Yellow" />
</MultiTrigger>
<MultiTrigger>
<!-- selected, and focused -->
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True" />
<Condition Property="IsSelected" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="Red" />
<Setter Property="Foreground" Value="White" />
</MultiTrigger>
</ControlTemplate.Triggers>

cannot set tooltip in style

I searched and find out I can't set the tooltip in setter.value directly (in a style.xaml file). However I can use static resource to set the tooltip.
My question is, since I need to supply dynamic text for the tooltip, I can't use static resource. How should I do that?
here is my example.
<Style x:Key="ErrorStyleRadius" TargetType="{x:Type FrameworkElement}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding (Validation.HasError), RelativeSource={RelativeSource Mode=Self}}" Value="True"/>
<Condition Binding="{Binding (Validation.Errors), RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource IsError}}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip>
<Label Content="{Binding somePropertyHere}"/>
</ToolTip>
</Setter.Value>
</Setter>
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ErrorControlTemplateRadiusError}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding (Validation.HasError), RelativeSource={RelativeSource Mode=Self}}" Value="True"/>
<Condition Binding="{Binding (Validation.Errors), RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource IsWarning}}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors), Converter={StaticResource ValMsg}}"/>
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ErrorControlTemplateRadiusWarning}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
for usage, i can use as
<textbox style={staticresource ErrorStyleRadius} text={bind name, validationOnDataError=true}/>
Why don't you try this -
<ToolTip x:Key="MyToolTip"
DataContext={Binding PlacementTarget, RelativeSource={RelativeSource Self}}>
<Label Content="{Binding Text}"/>
</ToolTip>
<Style x:Key="ErrorStyleRadius" TargetType="{x:Type FrameworkElement}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding (Validation.HasError), RelativeSource={RelativeSource Mode=Self}}" Value="True"/>
<Condition Binding="{Binding (Validation.Errors), RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource IsError}}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="ToolTip" Value={StaticResource MyToolTip}>
</Setter>
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ErrorControlTemplateRadiusError}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
You can give the property name in StaticResource. It will update the tooltip dynamically.
Also you can set the tooltip from your style if you want to set the text to Validation.Error like this -
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>

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>

Resources