Listbox Item selection - c#

I have a WPF project (C#, Visual Studio 2010, MVVM).
In this project I have a ListBox. When I select the items I am getting a blue box around my objects and I'm not sure where said box comes from. Obviously it's something to do with selection, but I'm not sure what to alter to get it to disappear.
The Listbox is here:
<ListBox ItemsSource="{Binding ChatNodeListViewModel.ChatNodeVMs, Source={StaticResource Locator}}" Background="Transparent" Name="LbNodes" SelectedItem="{Binding ChatNodeListViewModel.SelectedNode, Source={StaticResource Locator}}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas HorizontalAlignment="Left" VerticalAlignment="Top" Width="2000" Height="1600"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Canvas.Left" Value="{Binding XCoord}"/>
<Setter Property="Canvas.Top" Value="{Binding YCoord}"/>
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="lb_PreviewMouseLeftButtonDown" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Thumb Name="myThumb" Template="{StaticResource NodeVisualTemplate}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DragDelta">
<cmd:EventToCommand Command="{Binding ChatNodeListViewModel.DragDeltaCommand, Source={StaticResource Locator}}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Thumb>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding NodeVisualMode}" Value="0">
<Setter TargetName="myThumb" Property="Template" Value="{StaticResource NodeVisualTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding NodeVisualMode}" Value="1">
<Setter TargetName="myThumb" Property="Template" Value="{StaticResource NodeTemplateTest}" />
</DataTrigger>
<DataTrigger Binding="{Binding NodeVisualMode}" Value="2">
<Setter TargetName="myThumb" Property="Template" Value="{StaticResource NodeTemplateTest2}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
There is a main ControlTemplate which can be switched out for others (the main one being NodeVisualTemplate). But since this issue seems to happen on all of them, I'm inclined to believe it's further up the tree.
Still, I did do some testing for it, and will show it here:
<ControlTemplate x:Key="NodeVisualTemplate">
<Grid>
<Border x:Name="_Border" Padding="1" SnapsToDevicePixels="true" BorderThickness="3" Margin="2" CornerRadius="5,5,5,5" BorderBrush="SteelBlue" Visibility="Visible">
</Border>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="true">
<Setter TargetName="_Border" Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0" Color="Black" Opacity="1" BlurRadius="20" />
</Setter.Value>
</Setter>
<Setter TargetName="_Border" Property="Background" Value="Transparent"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Within the border at the top of this template are some text boxes and stuff, but I don't suspect them as being part of the affair. The point is that I've changed the background of this control template. It is 'transparent' in the XAML above, but I've changed it to red and other colours to test. It doesn't get rid of the blue square that surrounds the ListBoxItem.
Here is an image of the horrible blue background I'm getting:
Can I get rid of it? Honestly I'm really at a loss.
Thanks.
Edit: I should mention something else I tried that is rather important here. I went up a layer into the ListBox.ItemContainerStyle part above and just under this line:
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="lb_PreviewMouseLeftButtonDown" />
I added this:
<Style.Triggers>
<Trigger Property="IsSelected" Value="True" >
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
Did it work? No, it didn't. It didn't get rid of the big blue box. It did make my fonts bold though, so I know it at least had that effect.

Your Thumb is still generated inside of a ListBoxItem, Which has ControlTemplate of It's own.
You need to put in your ListBoxItem Style, a Setter for Template, and put in there only:
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter ContentSource="Content"/>
</ControlTemplate>

I think this is a "standard" thing, wpf does for you in ListBoxes. It highlights the currently selected Item for you. I found a similar thread here: WPF ListBox, how to hide border and change selected item background color?
They fixed it by adding this code to the listbox and setting the HighlightBrushKey to Transparent for this ListBox, did you try that in your case?
<ListBox BorderThickness="0" HorizontalContentAlignment="Stretch" >
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
</Style.Resources>
</Style>
</ListBox.Resources>
</ListBox>

Related

WPF INotifyDataErrorInfo highlight ListBoxItem

I've got a ListBox as such:
<ListBox Margin="5" ItemsSource="{Binding NetworkAdapters, Mode=OneWay}" SelectedItem="{Binding SelectedNetworkAdapter}" SelectionChanged="{s:Action SelectedNetworkAdapterChanged}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Width="15" Height="15" Margin="5">
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Fill" Value="Gray"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="{x:Static wpf:NetworkAdapterStatus.Up}">
<Setter Property="Fill" Value="Green"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="{x:Static wpf:NetworkAdapterStatus.Down}">
<Setter Property="Fill" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<StackPanel Margin="5,0,0,0">
<TextBlock Text="{Binding Name}" FontWeight="Bold"/>
<TextBlock Text="{Binding Description}"></TextBlock>
<TextBlock Text="{Binding Speed, StringFormat='Speed: {0}'}" FontSize="10"/>
<TextBlock Text="{Binding Status}" FontSize="10"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
NetworkAdapters is a collection of View Models that implement INotifyDataErrorInfo.
With the current XAML, if there is an error in any of the View Models the whole ListBox will be highlighted red, but I would like just the single ListBoxItems that contains errors to be highlighted.
I had a look at similar questions such as:
WPF ListBox ErrorTemplate and
Validating a ListBoxItem rather than a ListBox
But I still can't make this work. Any help would be appreciated.
UPDATE:
As per Krzysztof's advice, I tried wrapping the StackPanel around a border and using a DataTrigger as such:
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1">
<Border.Resources>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding HasErrors}" Value="True">
<Setter Property="BorderBrush" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Resources>
<StackPanel> ... </StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
However, what this produces is the following:
Which is slightly different from what I expected. I would like to have the highlight around the whole ListBoxItem not just part of it as per the image.
You need to implement an ItemContainerStyle as below:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding Validation.HasErrors}" Value="True">
<Setter Property="BorderBrush" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
This will enable you to change the border of the ListBoxItem itself, so the whole things as you want.
You can forget about ErrorTemplate and just use DataTrigger to bind to Validation.HasErrors:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type StackPanel}" BasedOn="{StaticResource {x:Type StackPanel}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Validation.HasErrors}" Value="True"> <!-- change all text to red -->
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
</StackPanel>
If you want a highlight, you can wrap StackPanel with a Border and set its color to red in a style.

XAML Style Won't Apply Until EventTrigger Command Completes

I have a ListBox which defines a custom ControlTemplate. The selected item has a style that changes the background and foreground, and the style basically works. However, I want to introduce a behaviour that displays a modal message box on selection changed, asking the user if they really want to select a different item. I've implemented an ICommand to do this which in the code below is shown as AreYouSureCommand.
The problem is whilst that modal message box is shown, the background style for the selected item is changed but the foreground is not. As soon as I dismiss the modal message box, the foreground colour changes. I haven't included the code for the ICommand because it's a bit convoluted but hopefully it is sufficient to say that a Window is opened with ShowDialog when it is executed.
Can anybody shed any light on why my background colour changes but not my foreground colour?
<ListBox x:Name="SubMenu" ItemsSource="{Binding MyItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName}"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="MainBorder">
<ContentControl x:Name="Presenter">
<ContentPresenter />
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<!-- Setter on MainBorder applies before AreYouSureCommand completes -->
<Setter TargetName="MainBorder" Property="Background" Value="Red" />
<!-- Setter on Presenter applies after AreYouSureCommand completes -->
<Setter TargetName="Presenter" Property="Foreground" Value="Green" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding AreYouSureCommand}"
CommandParameter="{Binding SelectedItem, ElementName=SubMenu}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
I eventually came to a solution for this. Basically I use another setter for the IsSelected property to push the value of IsSelected for the ListViewItem onto the view model. Then I use a DataTrigger instead of a regular Trigger to set the selected styles, and bind the trigger to the IsSelected property on the view model rather than the property of the ListViewItem itself. I don't really know why this works - it shouldn't be any different really, but it does work.
Thanks Juan and Ben for your comments.
<ListBox x:Name="SubMenu" ItemsSource="{Binding MyItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName}"
Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="MainBorder">
<ContentControl x:Name="Presenter">
<ContentPresenter />
</ContentControl>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="MainBorder" Property="Background" Value="Red" />
<Setter TargetName="Presenter" Property="Foreground" Value="Green" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding AreYouSureCommand}"
CommandParameter="{Binding SelectedItem, ElementName=SubMenu}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>

How to bind CustomControl DataTrigger to viewmodel DataContext

Using MVVM light, I have a control template for a custom control. There are multiple instances of this control. The style is set in Generic.xaml.
<Style TargetType="{x:Type tgvw:TimeSlotRect}">
<Setter Property="DataContext" Value="{Binding TimeSlotRectViewModel, Mode=OneWay, Source={StaticResource Locator}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type tgvw:TimeSlotRect}">
<Border Background="{TemplateBinding Background}"
Name="TimeSlotBorder"
BorderBrush="#A5BFE1"
BorderThickness="0,0.5,0,0.5" Height="30">
<Rectangle Fill="Beige" Name="Rect">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<cmd:EventToCommand Command="{Binding MouseDownCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Rectangle>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding DataContext.IsSelected}" Value="True" >
<Setter Property="Fill" Value="Black" TargetName="Rect"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The idea is that the mouse button click fires a command in the viewmodel that sets it's IsSelected property to true and then the rectangle should pick that up and change it's fill to black - but it doesn't seem to work.
If I bind the DataTrigger to the viewmodel like this:
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding TimeSlotRectViewModel.IsSelected, Source={StaticResource Locator}}" Value="True" >
<Setter Property="Fill" Value="Black" TargetName="Rect"/>
</DataTrigger>
</ControlTemplate.Triggers>
It fires and every control changes to black. Any ideas what I'm doing wrong?

Alternation row colors on ItemsControl by only applying a reusable Style

It is a very common thing people do and I understand how it works. I just can't find a way to make it work by only the use of a Style.
Lets say I have this ItemsControl:
<ItemsControl ItemsSource="{Binding stuffList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid ... />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If I wanted to add an alternating row color I would do something like this:
<ItemsControl ItemsSource="{Binding stuffList}" AlternationCount="2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="InnerGrid" ... />
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="Blue" TargetName="InnerGrid"/>
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="Red" TargetName="InnerGrid"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But I want to clear the xaml from this unwanted additionnal code. I will also need to re-use this mechanic. What do I do? I make a Style!
So, I want to apply a style to the ItemsControl like this:
<ItemsControl ItemsSource="{Binding stuffList}" Style="{StaticResource AlternateStyle}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid ... />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
and let the magic happen in a stylesheep somewhere else like that:
<Style TargetType="ItemsControl" x:Key="AlternateStyle">
<Setter Property="AlternationCount" Value="2"/>
<Style.Resources>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="0">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)}" Value="1">
<Setter Property="Background" Value="Blue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
But it doesn't work.. I'm not able to find the correct Binding or TargetType for the Style (that would replace Grid here). I'm out of idea and can't find help where this is not handled in the xaml. I could use some help from experienced WPF programmers.
Thanks in advance.

Change OnClick label color WPF

Im coming from a C# winforms background and I would normally do all this in code.
I have several Labels that I'm using as a menu.
When the mouse hovers over them the text changes color by:
<Page.Resources>
<SolidColorBrush x:Key="normalColor" Color="White" />
<SolidColorBrush x:Key="mouseOverColor" Color="Gold" />
<Style TargetType="Label" x:Key="menuItemStyle">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Foreground" Value="{StaticResource normalColor}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{StaticResource mouseOverColor}"/>
</Trigger>
</Style.Triggers>
</Style>
</Page.Resources>
<Label x:Name="Label_Video1" Style="{StaticResource menuItemStyle}" Content="1.Video 1." FontSize="16" HorizontalAlignment="Left" Margin="25,74,0,0" VerticalAlignment="Top" MouseLeftButtonDown="Label_Video1_MouseLeftButtonDown" />
<Label x:Name="Label_Video2" Style="{StaticResource menuItemStyle}" Content="2. Video 2." FontSize="16" HorizontalAlignment="Left" Margin="25,105,0,0" VerticalAlignment="Top" MouseDown="Label_Video2_MouseDown"/>
When the user clicks a label I want it to stay a certin color (In this case Gold) and all the others to stay their normal colour. So if a label has been previously clicked and I click another label it will go from gold to white etc
When using WPF, you have to think slightly differently. We know that the Label control doesn't know when it has been clicked and it especially doesn't know when another Label element has been clicked... but some controls do. Thinking about it... a RadioButton has exactly this behaviour. Now this is where WPF really shines.
We can use RadioButtons and by providing them with a new ControlTemplate, we can make them look like plain text:
<Grid Background="Black">
<Grid.Resources>
<Style TargetType="{x:Type RadioButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<TextBlock Text="{TemplateBinding Content}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType={
x:Type RadioButton}}}" Value="True">
<Setter Property="Foreground" Value="Gold" />
</DataTrigger>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource
AncestorType={x:Type RadioButton}}}" Value="True">
<Setter Property="Foreground" Value="Gold" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<StackPanel Margin="5" Background="{x:Null}">
<RadioButton Content="1.Video 1." />
<RadioButton Content="2.Video 2." />
<RadioButton Content="3.Video 3." />
</StackPanel>
</Grid>
If you don't want the RadioButtons to be all together in a StackPanel, then you can use give them the same GroupName property value to ensure that they still operate as one group.

Categories

Resources