Wpf listbox itemstlye triggers not working - c#

I am using a listbox itemstyle and triggers to change the mouseover color but the style does not seem to work:
<ListBox ItemsSource="{Binding LocationItems}" SelectionChanged="RadListBox_SelectionChanged" Name="RLBLocations">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="BorderBrush" Value="#FF709A70"/>
<Setter Property="Height" Value="50"/>
<Setter Property="Width" Value="200"/>
<Setter Property="Foreground" Value="#FF5C5C5C"/>
<Setter Property="FontFamily" Value="Franklin Gothic Book"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<StackPanel Orientation="Horizontal" Height="50">
<Image Source="{Binding Icon}" Margin="0" Stretch="UniformToFill" Width="32" Height="32"/>
<TextBlock Margin="10,10,0,0" Text="{Binding Text.Text}" Foreground="Black" TextAlignment="Left"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ToolTip" Value="{Binding ToolTip}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="#FFF0F0F0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}},
Path=IsMouseOver}" Value="True">
<Setter Property="Background" Value="#FF709A70"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I get this error in the output window:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ListBoxItem', AncestorLevel='1''. BindingExpression:Path=IsMouseOver; DataItem=null; target element is 'ListBoxItem' (Name=''); target property is 'NoTarget' (type 'Object')
I understand that the error but need help trying figure out the style - I have tried doing it with a datatemplate and that did not work out either.

Your DataTrigger:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}},
Path=IsMouseOver}"
Value="True">
<!-- ... -->
</DataTrigger>
Is trying to find the Ancestor (of Type ListBoxItem) of the ListBoxItem. Of course that doesn't exist unless you nest ListBoxItems inside of each other.
Change it to this:
<Trigger Property="IsMouseOver" Value="True">
<!-- ... -->
</Trigger>
Also, you're overriding the ListBoxItem's Template, and in your custom template there's nothing pointing to the ListBoxItem.Background property, that brush will never be visible on screen.
I suggest you use it for the StackPanel's Background:
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<StackPanel Orientation="Horizontal" Height="50" Background="{TemplateBinding Background}">
<!-- ... -->
</StackPanel>
</ControlTemplate>
Also, I think you might be confusing the concept of ListBoxItem.Template with ListBox.ItemTemplate, which is a DataTemplate used to render your Data inside regular ListBoxItems. I suggest you take a look at this tutorial for more info.

Related

WPF TextBox selected text does not work

I have a readonly TextBox control with a static resource, looks like this:
<Style TargetType="TextBox" x:Key="MyEditTextEditor">
<Setter Property="FontFamily" Value="{Binding Path=TextEditorFontFamily, ElementName=LogViewerProperty}" />
<Setter Property="FontWeight" Value="{Binding Path=TextEditorFontWeight, ElementName=LogViewerProperty}" />
<Setter Property="FontSize" Value="{Binding Path=TextEditorFontSize, ElementName=LogViewerProperty}" />
<Setter Property="FontStyle" Value="{Binding Path=TextEditorFontStyle, ElementName=LogViewerProperty}" />
<Setter Property="TextWrapping" Value="{Binding Path=WordWrapping, ElementName=LogViewerProperty, Converter={StaticResource BoolToTextWrap}}" />
<Setter Property="Text" Value="{Binding Message, Mode=OneWay}" />
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="Visibility" Value="Collapsed" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<TextBox Text="{TemplateBinding Text}" BorderThickness="0" Margin="-3,-1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Opacity="0.4" Color="{Binding Source={x:Reference LogViewerProperty}, Path=TextEditorSelectionColor}" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
It's embedded into a ListBox. All works fine, but when I want to get the selected text, the property is always string.Empty. The SelectionChanged event works perfect. Any suggestions? At the moment I do not know, why the SelectedText is string.Empty.
Here is the SelectionChanged event
private void ReadOnlyEditor_SelectionChanged(object sender, RoutedEventArgs e)
{
LOG.Debug("Current text {3}; selection {0} length {1}, start {2}", readOnlyEditor.SelectedText, readOnlyEditor.SelectionLength, readOnlyEditor.SelectionStart, readOnlyEditor.Text);
}
And yes, the text is selected in the control, but the property is empty. In readOnlyEditor.Text exists the right text.
Put your data binding into the ListBoxItem, then call the data from the ListBoxItem. Then the whole ListBoxItem is selectable. e.g.
<ListBox SelectionChanged="ListItemSelected" ItemTemplate="{StaticResource SelectedTextTemplate}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
.../...
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Build your data template or control template outside of the ListBox e.g.
<DataTemplate x:Key="SelectedTextTemplate">
<TextBox Text="{Binding SelectedText}"/>
</DataTemplate>

WPF CustomControl switch content of ContentControl

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomControlLibrary1">
<ContentControl x:Key="BackSide" Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Back}" RenderTransformOrigin="0.5,0.5">
<ContentControl.RenderTransform>
<ScaleTransform ScaleX="-1" />
</ContentControl.RenderTransform>
</ContentControl>
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<ContentControl Grid.Row="1">
<ContentControl.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="tf" ScaleX="1" />
</TransformGroup>
</ContentControl.RenderTransform>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Content" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Front}" />
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<Binding ElementName="tf" Path="ScaleX">
<Binding.Converter>
<loc:LessThanXToTrueConverter X="0" />
</Binding.Converter>
</Binding>
</DataTrigger.Binding>
<DataTrigger.Setters>
<Setter Property="Content" Value="{StaticResource BackSide}"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This is some XAML code from a customcontrol. Where there are two dependency properties (Front and Back).
Via my DataTrigger I want to change the Content of the ContentControl from using "Front" to using "Back".
At first it shows the depencency property "Front" and then it should use the depencency property "Back" as the Content.
This is done via this code:
<DataTrigger.Setters>
<Setter Property="Content" Value="{StaticResource BackSide}"/>
</DataTrigger.Setters>
But this doesn't work...
I can bind and display the content of the Front dependency property in my control via:
<Setter Property="Content" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Front}" />
but I can't figure out how to bind the DataTrigger setter so that it uses the ContentControl's Content with the x:Key="BackSide" ContentControl.
Thanks in advance.
You said that this XAML works:
<Setter Property="Content" Value="{Binding RelativeSource={RelativeSource
TemplatedParent}, Path=Front}" />
So why don't you just try putting this XAML into your DataTrigger?:
<Setter Property="Content" Value="{Binding RelativeSource={RelativeSource
TemplatedParent}, Path=Back}" />
UPDATE >>>
I'm not really sure what you're trying to do with your UIElement DependencyPropertys, but I am guessing that you are taking the wrong approach. Typically in WPF, our properties are data types, not UI types and then we generate UI types using DataTemplates... perhaps you should re-think your approach.
Please see the Data Templating Overview page on MSDN for further help.
You might also find my answer to the WPF MVVM navigate views question helpful to read.

WPF ItemsControl and Togglebuttons weird behaviour

I've been working on a WPF application but encountered some weird behaviour when using ItemsControl to display a list of togglebuttons with binding. It works just fine when it is displaying a collection of 1 element, but when I have two elements it behaves very strange. It will not show the image of both togglebuttons at once if they are in the same state (i.e. toggled on).
The relevant XAML. I have checked that the property behind is updated correctly, but let me know if there is any code that is needed.
<ItemsControl x:Name="ServiceItemsControl" Grid.Row="0" ItemsSource="{Binding DraftMessage.Services}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton IsChecked="{Binding Path=Selected}">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Focusable" Value="False"/>
<!--Setter Property="BorderThickness" Value="0"/-->
<Setter Property="Opacity" Value="1"/>
<Setter Property="Content">
<Setter.Value>
<Image Source="{Binding Path=Service.OtherLogo}" Width="25" Height="25"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content">
<Setter.Value>
<Image Source="{Binding Path=Service.Logo}" Width="25" Height="25"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You need to use a DataTrigger on the Image to change its Source property when the ToggleButton.IsChecked value is changed instead. Try this:
<DataTemplate>
<ToggleButton IsChecked="{Binding Path=IsSelected}">
<Image Width="25" Height="25">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="{Binding Service.OtherLogo}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked,
RelativeSource={RelativeSource AncestorType={x:Type
ToggleButton}}}" Value="True">
<Setter Property="Source" Value="{Binding Service.Logo}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Focusable" Value="False"/>
<!--Setter Property="BorderThickness" Value="0"/-->
<Setter Property="Opacity" Value="1"/>
</Style>
</ToggleButton.Style>
</ToggleButton>
</DataTemplate>

Change fontweight on button in WPF treeview

I have a templated treeview that is working just as i want, but i have a context menu which allows for a tier 1 node to be marked as "default".
I have a datatrigger which reacts to a property in the viewmodel, which should change the fontweight to bold just to visually show that this is the default node. But the setter will not change the fontweight on the button no matter what!
However if i change another value, like the foreground color for example, it works just fine, font size also no problem.
Why is this, can anyone explain this? Here is some code if needed:
Trigger:
<HierarchicalDataTemplate ItemsSource="{Binding Children,Mode=TwoWay,NotifyOnSourceUpdated=True}">
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<Image x:Name="nodeImg" Source="{Binding Image}" MaxHeight="16" MaxWidth="16"/>
<Button x:Name="nodeButton" Content="{Binding Name}" Command="{Binding Command}" Style="{StaticResource TreeMenuButton}"/>
</StackPanel>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem},Path=IsExpanded}" Value="True">
<Setter TargetName="nodeImg" Property="Source" Value="{Binding ImageExpanded}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsDefaultConnection}" Value="True">
<Setter TargetName="nodeButton" Property="FontWeight" Value="Bold"></Setter>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
Default style on button:
<Style x:Key="TreeMenuButton" TargetType="{x:Type Button}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="MinHeight" Value="23" />
<Setter Property="MinWidth" Value="75" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Border"
CornerRadius="0"
BorderThickness="0"
Background="Transparent"
BorderBrush="Transparent">
<ContentPresenter Margin="2"
HorizontalAlignment="Left"
VerticalAlignment="Center"
RecognizesAccessKey="True"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I'd be surprised if your code even built as you can't add a DataTrigger into a UIElement.Triggers collection. Try using a Style instead (this definitely works):
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsDefault}" Value="True">
<Setter Property="TextBlock.FontWeight" Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
If by some miracle your DataTemplate does work in a UIElement.Triggers collection, then try using the class name as well as the property name:
<Setter Property="TextBlock.FontWeight" Value="Bold" />

Attached Behavior in Trigger Causes Build Error

I have a Style with a control template and I am having trouble getting it to compile. I am trying to trigger an attached behavior. If I put it in the control template triggers it works fine...but if I put it in the textbox triggers I get a build error that says:
Cannot find the static member 'SelectAllProperty' on the type
'TextBoxBehavior'
Here is my code:
<Style x:Key="RenamingTextBox" TargetType="{x:Type TextBox}">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<TextBlock x:Name="block" Visibility="Visible" Text="{TemplateBinding Text}" Margin="0"/>
<TextBox x:Name="box" Visibility="Collapsed" Text="{TemplateBinding Text}" Margin="0">
<TextBox.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.Setters>
<Setter TargetName="box" Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}"/>
<!-- This next line gives an error even though it is the same format as the one below -->
<Setter Property="behaviors:TextBoxBehavior.SelectAll" Value="True"/>
</Trigger.Setters>
</Trigger>
</TextBox.Triggers>
</TextBox>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsRenaming}" Value="true">
<DataTrigger.Setters>
<Setter TargetName="block" Property="TextBox.Visibility" Value="Collapsed" />
<Setter TargetName="box" Property="TextBox.Visibility" Value="Visible" />
<!-- Uncommenting below works fine -->
<!--<Setter TargetName="box" Property="behaviors:TextBoxBehavior.SelectAll" Value="True"/>-->
</DataTrigger.Setters>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
Any ideas as to why one gives a build error and the other doesn't?
Nevermind, I needed to put the triggers for the textbox in a style instead:
<TextBox x:Name="box" Visibility="Collapsed" Text="{TemplateBinding Text}" Margin="0">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Setter Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}"/>
<Setter Property="behaviors:TextBoxBehavior.SelectAll" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

Categories