WPF ListViewItem Trigger Background - c#

I have the following simple window:
<Window x:Class="ListViewTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ListViewTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ListView BorderThickness="0">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="BorderThickness" Value="0"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListViewItem Content="Just a test"/>
<ListViewItem Content="Another test"/>
</ListView>
</Grid>
</Window>
So when the item is selected the background should become red and the border thickness should become "0". The thickness works but the background color does not work. Any idea what I did wrong here? It seems all brush related properties can not be applied (e.g. BorderBrush does also not work)

The default template for the ListViewItem is not set up to look at the Background property for the color of the item when its selected.
Its set up to look for a solid color brush with a key of "Item.SelectedActive.Background"
To check how the default style template is set up, right click on one of the items in your list in the designer, select Edit Template > Edit a Copy:
It will add some code to the top of your Window xaml under <Window.Resources>
You'll want to pay particularly close attention to the selected text below:
This is what controls the background color of the selected item. You can simply change the Color value to Red or whatever color you want for the highlighted color and you're off to the races.
You could create your own template but the built in one is pretty robust already.

Related

Why grid background color covers the whole window if set in application resources?

I'm wondering why setting background color of a grid in application resources results in whole window covered by grid background, even if I don't have grid panel specified in XAML main window file.
MainWindow.xaml:
<Window x:Class="TicTacToe.DesktopApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="Tic-tac-toe"
Height="420"
Width="420"
ResizeMode="NoResize"
WindowStyle="SingleBorderWindow">
<DockPanel>
<Button Content="Button"></Button>
</DockPanel>
</Window>
App.xaml:
<Application x:Class="TicTacToe.DesktopApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="10" />
</Style>
<Style TargetType="Grid">
<Setter Property="Background" Value="Red" />
<!--Uncomment the line below to see that button seems to be hidden under the grid.-->
<!--<Setter Property="Opacity" Value="0.5" />-->
</Style>
</Application.Resources>
</Application>
MainWindow.xaml.cs and App.xaml.cs contain only auto generated code. Nothing special.
Visual Studio preview shows window as expected:
Instead of it I'm getting:
Questions
Why it behaves like that? Is there somewhere hidden and always present grid that overlays whole window and gets included by my styling rules? And if so, why it does and why it is applied with the observable delay of a fragment of a second?
That is a grid used by the Visual tree design tools to select elements in the visual tree when debugging. You can verify this using an event setter,and clicking the grid, or by running the app, not in debug mode.
<Style TargetType="Grid">
<Setter Property="Background" Value="Red" />
<EventSetter Event="PreviewMouseDown" Handler="Grid_PreviewMouseDown"/>
<!--Uncomment the line below to see that button seems to be hidden under the grid.-->
<!--<Setter Property="Opacity" Value="0.5" />-->
</Style>
,
public partial class App : Application
{
private void Grid_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
MessageBox.Show(VisualTreeHelper.GetParent(sender as Grid).ToString());
}
}

WPF MenuItem Icon sharing

I want to bind icons to the MenuItem controls where these items are dynamically created. I tried to set the x:Shared attribute to False but always only the last item has icon.
Here is my style for the MenuItems ItemContainerStyle code:
<Window.Resources>
<Style TargetType="{x:Type MenuItem}" x:Key="MenuItemStyle" x:Shared="False">
<Setter Property="Icon">
<Setter.Value>
<Image Source="{Binding IconSource}" />
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
And the MenuItem definition:
<MenuItem Header="Workspaces" ItemsSource="{Binding WorkspaceItems}" Icon="{StaticResource BranchIcon}" ItemContainerStyle="{StaticResource MenuItemStyle}" />
I have already tried to set this Shared attribute on the Image control but no luck.
Any suggestion?
You are almost there!
First of all: don't be confuse by Template vs Style.
When you are setting Icon property to an Image control, only one copy is created. As a control can have only one parent, it is removed from the previous parent each time it's re-assigned.
That's why you see only one icon.
You have 2 solutions for what you want:
use datatemplate instead, and redefine the whole Template of a MenuItem
use a style with a shared image component (what you tried to achieve)
In your example the only error is that the Shared attribute should be false on the Image resource, not on the whole style. This should work:
<Window.Resources>
<Image x:Key="MenuIconImage" x:Shared="false" Source="{Binding IconSource}"/>
<Style TargetType="{x:Type MenuItem}" x:Key="MenuItemStyle" BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Icon" Value="{StaticResource MenuIconImage}">
</Setter>
</Style>
</Window.Resources>
Hope it helps.

Global TextBlock style messing with named Label style

I created a very simple WPF application with the following resources:
<Application x:Class="StyleTest.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<FontFamily x:Key="MainFontFamily">Calibri</FontFamily>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontFamily" Value="{StaticResource MainFontFamily}" />
</Style>
<Style x:Key="HyperlinkLabel" TargetType="{x:Type Label}">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Foreground" Value="Yellow" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Application.Resources>
The TextBlock style doesn't have a x:Key property. I want this property to apply to all the TextBlocks.
The UI is simply:
<Window x:Class="StyleTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Test 123 Test 123" Style="{StaticResource HyperlinkLabel}" />
</Grid>
</Window>
When I run the application:
"Test 123 Test 123" is displayed in yellow.
When I put the mouse cursor over the text, the mouse cursor icon changes to a hand with a pointing finger.
When I put the mouse cursor over the text, the text turns red.
Great. But if I changed the first style from:
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontFamily" Value="{StaticResource MainFontFamily}" />
</Style>
to
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontFamily" Value="{StaticResource MainFontFamily}" />
<Setter Property="Foreground" Value="Blue" />
</Style>
Then, when running the application:
The text is displayed in blue instead of yellow. This is bad.
When I put the mouse cursor over the text, the mouse cursor icon changes to a hand with a pointing finger. This is ok.
When I put the mouse cursor over the text, the text stays blue. This is bad.
Why is the TextBlock style messing up with the Label style? I seems that Label inherits from TextBlock. But even if it is the case, the Label style should be used no?
How can I “force” the use of the Label style? How can the Label style overwritte the TextBlock style?
Thanks!
Implicit styles in Application.Resources applied to non-control items such as TextBlock do not respect control boundaries, and will apply to the entire application regardless of what other definitions exist. This was probably done to allow for global application-wide styling, such as font, text sizes, colors, etc.
Two solutions to fix this :
Change implicit style to a Label, which inherits from Control. Because it is a Control, it will respect control boundaries and not try to overwrite the properties of every text element in your application.
Move the style to Window.Resources instead of Application.Resources. This way the style isn't considered global for the entire application, so isn't treated as such when deciding how to render items.
I had the same question a while back, and this is the best I could make of the description given. Implicit styles in Application.Resources vs Window.Resources? :)

Click on WPF Element with lower ZOrder and without using IsHitTestVisible

To simplify the problem, I have the following XAML Windows :
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{StaticResource MainViewModel}">
<Grid>
<Button Click="Button_Click">Click Me</Button>
<ListBox ItemsSource="{Binding Items}" Background="{x:Null}">
</ListBox>
</Grid>
</Window>
When it runs it look like this :
What I would expect is:
I'm able to select Items ( WpfApplication1.BusinessObject )
AND also able click on the button ( as I marked Background as {x:Null} )
But I can't click on the button, only select items in the listbox.
Note: If I use IsHitTestVisible, I can click on the button, but no more on the items, which makes sense. I also absolutely need to have both listbox and button taking the whole windows. Having on top the listbox and on bottom the button is not a solution.
Is there a good way to have this working ?
Thanks for your help !
you can try putting your listbox and button inside a StackPanel
also, it does not seem like your button is bound to any actions, could that be the reason : )?
Another option if you want the listbox sitting on top of and partially covering your button is to set `VerticalAlignment="Top" on your listbox. This will allow the listbox to grow vertically and only the height of the listbox will cover the button. However, if the listbox has a lot of items then it will completely cover the button and you will not be able to click the button.
You could always add PreviewMouseDown handler on the listbox and trigger the button click or command. That way if the listbox fills the entire window, you'll still be able to trigger the button handler.
Have a look at this thread: Canvas in ScrollViewer (Preview)MouseButtonDown event order
The problem is the ScrollViewer that handles mouse events. You could implement your custom ListBox template and use a custom ScrollViewer derived class with the code changes shown in the mentioned thread.
Thanks dotsven, setting handle=false didn't work for me, but I found this question that helped me: Click through ListBox to underlying Control. Taking inspiration from this I removed the ScrollViewer by setting a new style to my listbox and it worked :
<Style x:Key="{x:Type ListBox}" TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1" SnapsToDevicePixels="true">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I also had to keep the background to {x:Null} :
<ListBox ItemsSource="{Binding Items}" Background="{x:Null}" Height="65" Width="242"/>
Thanks all for your help !

WPF Listbox with wider scrollbars

I'm totally new on WPF and I need your help for creating a wpf custom ListBox with scrollbar wider than the default.
I've found a solution that works fine for a Window WPF including a ListBox:
<Window x:Class="iFixCustomControlsTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:iFixCustomControls;assembly=iFixCustomControls"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox HorizontalAlignment="Left" Height="92" Margin="56,88,0,0" VerticalAlignment="Top" Width="357" ScrollViewer.VerticalScrollBarVisibility="Visible"/>
</Grid>
<Window.Resources>
<Style TargetType="ScrollBar">
<Setter Property="Width" Value="100"/>
</Style>
</Window.Resources>
</Window>
This solution is not the my favorite one, because it implies to write code in a Window including a "classic" Listbox. What I need is a way to modify scrollbar inside the Listbox (if I understood fine) in Generic.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:iFixCustomControls">
<Style TargetType="local:iFixCustomListBox" BasedOn="{StaticResource {x:Type ListBox}}">
<!--
Setting scrollbar wider than default
Something like:
<Style TargetType="ScrollBar">
<Setter Property="Width" Value="100"/>
</Style>
-->
</Style>
</ResourceDictionary>
.cs file is:
public class iFixCustomListBox : ListBox
{
static iFixCustomListBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(iFixCustomListBox), new FrameworkPropertyMetadata(typeof(iFixCustomListBox)));
}
}
Is this approach correct or a better way should involve User Control instead Custom Controls?
If I understand you correctly you have a custom control type derived from ListBox and you want every instance of that control to have a wider vertical scrollbar.
If so, you can just use a custom style for your control (which you probably have already), and add a ScrollBar style to that style's Resources collection:
<Style TargetType="{x:Type local:iFixCustomListBox}">
<Style.Resources>
<Style TargetType="{x:Type ScrollBar}">
<Setter Property="Width" Value="100" />
</Style>
</Style.Resources>
</Style>
I tried with this style placed in the resources collection of (a) a window, and (b) the application, and it worked fine in both cases, so I assume it would also work if placed in generic.xaml.
What about this?
<ScrollViewer Width="100">
<ListBox ...>
</ScrollViewer>

Categories

Resources