Answered How to style ListView selection?

  • Sunday, November 15, 2009 6:37 AM
     
      Has Code

    I am just starting out with WPF and am stuck on applying a style to a ListView.  I simply want to change the Background of the selected item(s) to a different color than the default.

    The XAML below fails to give me what I expected: clicking on the list view item should turn the selected item red.  I know the style is attached to the ListViewItem--if I change "IsSelected" to "IsMouseOver", it works fine.

    I am missing something very basic here.  What is it?

    <Page
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      
      <Page.Resources>
            <Style TargetType="{x:Type ListViewItem}">
                <Style.Resources>
                    <SolidColorBrush x:Key="SelectedBackgroundBrush" Color="DarkCyan" />
                </Style.Resources>
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="true">
                        <Setter Property="Background" Value="Red">
                        </Setter>
                    </Trigger>
                </Style.Triggers>
            </Style>
      </Page.Resources>  
      
      <Grid>
      <ListView>
        <ListViewItem>Item 1</ListViewItem>    
        <ListViewItem>Item 2</ListViewItem>
        <ListViewItem>Item 3</ListViewItem>    
      </ListView>
      </Grid>
    </Page>

    // Richard | www.attilan.com, Attilan Software Factory

Answers

  • Sunday, November 15, 2009 8:05 AM
     
     Answered

    Since the ListViewItem's base type ListBoxItem changes a Border background in control template instead of ListBoxItem.Background on the selected item. A workaround could be modifying SystemColors.HighlightBrush since ListBoxItem's control template uses it. Please see example below:

    Markup:
    <Window x:Class="ChangeSelectedListViewItemBackgroundColor.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
        <Window.Resources>
            <Style TargetType="ListViewItem">
                <Style.Resources>
                    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red"/>
                </Style.Resources>
            </Style>
        </Window.Resources>
        <StackPanel>
            <ListView>
                <ListViewItem>Item 1</ListViewItem>
                <ListViewItem>Item 2</ListViewItem>
                <ListViewItem>Item 3</ListViewItem>
            </ListView>
        </StackPanel>
    </Window>


    William
  • Sunday, November 15, 2009 6:57 PM
     
     Answered Has Code
    Hi Richard,

    Olaf, thanks for helping me out.  I tried your ResourceDictionary, and I see the mouse over and selection gradient effects--beautiful!

    One problem I have, and maybe this is because I tested it with regular listview instead of a gridview.  I see the rows where the list view items are, but no text!  With no hover/selection, it's a grey row.  Even in hover/selected mode, I see the gradients, but no text.

    All I did was save your XAML resource dictionary, and referenced it from a little test app in Kaxaml.  Did I do something wrong or do I have to change a style for the foreground text?

    that's due to the fact that my style is actually overriding the ControlTemplate and me not bothering for anything but a ListView with explicitly defined columns. :-)
    Since you yourself have not defined a GridViewColumn, the ListView doesn't have anything to show.
    You'll have to amend your <ListView>-tag so that it contains a GridViewColumn, i.e.:

          <ListView>
             <ListView.View>
                <GridView AllowsColumnReorder="False">
                   <GridViewColumn Header="TestColumn"/>
                </GridView>
             </ListView.View>
             <ListViewItem>Item 1</ListViewItem>
             <ListViewItem>Item 2</ListViewItem>
             <ListViewItem>Item 3</ListViewItem>
          </ListView>

    Also, note that you won't see the IsSelected- and Hover-effects in the designer - you'll have to run your app.


    Cheers,
    Olaf
    • Marked As Answer by Richard Guion Sunday, November 15, 2009 10:07 PM
    •  
  • Sunday, November 15, 2009 11:33 PM
     
     Answered
    Hi Richard,

    yes, it's the GridViewRowPresenter that causes the effect. That is, the Template then only considers GridViewRows.
    If you replace its definition with <ContentPresenter/> , you would see the items without having to add the GridView(Column), i.e. as in your first attempt to use my style.

    Cheers,
    Olaf
    • Marked As Answer by Richard Guion Monday, November 16, 2009 4:11 AM
    •  

All Replies

  • Sunday, November 15, 2009 8:05 AM
     
     Answered

    Since the ListViewItem's base type ListBoxItem changes a Border background in control template instead of ListBoxItem.Background on the selected item. A workaround could be modifying SystemColors.HighlightBrush since ListBoxItem's control template uses it. Please see example below:

    Markup:
    <Window x:Class="ChangeSelectedListViewItemBackgroundColor.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
        <Window.Resources>
            <Style TargetType="ListViewItem">
                <Style.Resources>
                    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red"/>
                </Style.Resources>
            </Style>
        </Window.Resources>
        <StackPanel>
            <ListView>
                <ListViewItem>Item 1</ListViewItem>
                <ListViewItem>Item 2</ListViewItem>
                <ListViewItem>Item 3</ListViewItem>
            </ListView>
        </StackPanel>
    </Window>


    William
  • Sunday, November 15, 2009 10:50 AM
     
      Has Code
    Hi Richard,

    in addition to William, here's what I'm using in an application:

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
       <SolidColorBrush x:Key="ListView_DefaultControlBorderBrush" Color="#FF688CAF"/>
       <LinearGradientBrush x:Key="ListView_DefaultControlBackgroundBrush" StartPoint="0,0" EndPoint="1,1">
          <LinearGradientBrush.GradientStops>
             <GradientStop Offset="0.0" Color="White"/>
             <GradientStop Offset="1.1" Color="#FFE5ECFF"/>
          </LinearGradientBrush.GradientStops>
       </LinearGradientBrush>
       <SolidColorBrush x:Key="ListView_DefaultControlForegroundBrush" Color="#FF10257F"/>
    
       <!-- General Style for all items in the ListView control -->
       <Style TargetType="{x:Type ListViewItem}" >
          <!-- 
                This allows for using an item's HorizontalAlignment property to align items within 
                the ListView by stretching the content to fill the available horizontal space. 
            -->
          <Setter Property="HorizontalContentAlignment" Value="Stretch" />
          <Setter Property="Template">
             <Setter.Value>
                <ControlTemplate TargetType="ListBoxItem">
                   <Border x:Name="Border" Padding="0,5,0,5" SnapsToDevicePixels="true"
                           Background="#F0F0FF" CornerRadius="3" Margin="2" BorderThickness="1">
                      <GridViewRowPresenter VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                   </Border>
                   <ControlTemplate.Triggers>
                      <Trigger Property="IsSelected" Value="True">
                         <Setter Property="Foreground" Value="White"/>
                         <Setter TargetName="Border" Property="BorderBrush" Value="DarkBlue"/>
                         <Setter TargetName="Border" Property="Background">
                            <Setter.Value>
                               <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                                  <GradientStop Color="#FFBED7FE" Offset="0.1"/>
                                  <GradientStop Color="#FF6C82CC" Offset="0.51"/>
                                  <GradientStop Color="#FF5567A1" Offset="0.986"/>
                               </LinearGradientBrush>
                            </Setter.Value>
                         </Setter>
    
                      </Trigger>
                      <Trigger Property="IsMouseOver" Value="True">
                         <Setter Property="Foreground" Value="DarkBlue" />
                         <Setter TargetName="Border" Property="BorderBrush" Value="DarkOrange"/>
                         <Setter TargetName="Border" Property="Background">
                            <Setter.Value>
                               <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                                  <GradientStop Color="#FFFFD5A0" Offset="0.3"/>
                                  <GradientStop Color="#FFFFA335" Offset="0.51"/>
                                  <GradientStop Color="#FFFFC14A" Offset="0.986"/>
                               </LinearGradientBrush>
                            </Setter.Value>
                         </Setter>
                      </Trigger>
                   </ControlTemplate.Triggers>
                </ControlTemplate>
             </Setter.Value>
          </Setter>
       </Style>
    
       <!-- General appearance of ListView controls -->
       <Style TargetType="{x:Type ListView}">
          <Setter Property="Background" Value="{StaticResource ListView_DefaultControlBackgroundBrush}"/>
          <Setter Property="Foreground" Value="{StaticResource ListView_DefaultControlForegroundBrush}"/>
          <Setter Property="BorderBrush" Value="{StaticResource ListView_DefaultControlBorderBrush}" />
          <Setter Property="BorderThickness" Value="1" />
       </Style>
    
       <!-- General appearance of ListView ColumnHeaders -->
       <Style TargetType ="GridViewColumnHeader">
          <Setter Property="Padding" Value="3,1,3,1"/>
          <Setter Property="HorizontalContentAlignment" Value="Left"/>
          <Setter Property="VerticalContentAlignment" Value="Center"/>
          <Setter Property ="Background" Value ="LightSlateGray" />
          <Setter Property ="Foreground" Value ="White" />
          <Style.Triggers>
             <Trigger Property ="IsMouseOver" Value ="True" >
                <Setter Property ="Background" Value ="#FFF2A73A" />
                <Setter Property ="Foreground" Value ="Black" />
             </Trigger>
          </Style.Triggers>
       </Style >
    </ResourceDictionary>
    

    Since this is a ResourceDictionary, place it in a separate file and link to it in your Application.XAML (VB) resp. App.XAML (C#):

    <Application 
        x:Class="WpfTests.App"
        StartupUri="MainWindow.xaml"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfTests"
        >
    
       <Application.Resources>
          <ResourceDictionary>
             <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Resources/Themes/Default/ListView.xaml" />
             </ResourceDictionary.MergedDictionaries>
          </ResourceDictionary>
       </Application.Resources>
    </Application>
    
    


    Cheers,
    Olaf
  • Sunday, November 15, 2009 6:42 PM
     
     
    Thank you, William--that worked very well!
    // Richard | www.attilan.com, Attilan Software Factory
  • Sunday, November 15, 2009 6:47 PM
     
      Has Code
    Olaf, thanks for helping me out.  I tried your ResourceDictionary, and I see the mouse over and selection gradient effects--beautiful!

    One problem I have, and maybe this is because I tested it with regular listview instead of a gridview.  I see the rows where the list view items are, but no text!  With no hover/selection, it's a grey row.  Even in hover/selected mode, I see the gradients, but no text.

    All I did was save your XAML resource dictionary, and referenced it from a little test app in Kaxaml.  Did I do something wrong or do I have to change a style for the foreground text?

    <Page
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      
       <Page.Resources>
          <ResourceDictionary>
             <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="C:/Projects/samples/WPF/ListViewStyles/ListViewStyle.xaml" />
             </ResourceDictionary.MergedDictionaries>
          </ResourceDictionary>
       </Page.Resources>
      
      <Grid>  
      <ListView>
        <ListViewItem>Item 1</ListViewItem>    
        <ListViewItem>Item 2</ListViewItem>
        <ListViewItem>Item 3</ListViewItem>    
      </ListView>  
      </Grid>
    </Page>

    // Richard | www.attilan.com, Attilan Software Factory
  • Sunday, November 15, 2009 6:57 PM
     
     Answered Has Code
    Hi Richard,

    Olaf, thanks for helping me out.  I tried your ResourceDictionary, and I see the mouse over and selection gradient effects--beautiful!

    One problem I have, and maybe this is because I tested it with regular listview instead of a gridview.  I see the rows where the list view items are, but no text!  With no hover/selection, it's a grey row.  Even in hover/selected mode, I see the gradients, but no text.

    All I did was save your XAML resource dictionary, and referenced it from a little test app in Kaxaml.  Did I do something wrong or do I have to change a style for the foreground text?

    that's due to the fact that my style is actually overriding the ControlTemplate and me not bothering for anything but a ListView with explicitly defined columns. :-)
    Since you yourself have not defined a GridViewColumn, the ListView doesn't have anything to show.
    You'll have to amend your <ListView>-tag so that it contains a GridViewColumn, i.e.:

          <ListView>
             <ListView.View>
                <GridView AllowsColumnReorder="False">
                   <GridViewColumn Header="TestColumn"/>
                </GridView>
             </ListView.View>
             <ListViewItem>Item 1</ListViewItem>
             <ListViewItem>Item 2</ListViewItem>
             <ListViewItem>Item 3</ListViewItem>
          </ListView>

    Also, note that you won't see the IsSelected- and Hover-effects in the designer - you'll have to run your app.


    Cheers,
    Olaf
    • Marked As Answer by Richard Guion Sunday, November 15, 2009 10:07 PM
    •  
  • Sunday, November 15, 2009 10:26 PM
     
      Has Code
    Thank you, Olaf!  I tried adding the column and it works perfectly.

    I want to make sure I understand why the style only affects ListViews with a GridViewColumn.  After looking at your style, I think it's the section below.

    I read this as saying a ListBoxItem ControlTemplate has-a defined border and has-a GridViewRowPresenter?  Thus, a plain old listview has nothing to show, as you said.

                <ControlTemplate TargetType="ListBoxItem">
                   <Border x:Name="Border" Padding="0,5,0,5" SnapsToDevicePixels="true"
                           Background="#F0F0FF" CornerRadius="3" Margin="2" BorderThickness="1">
                      <GridViewRowPresenter VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                   </Border>
    

    // Richard | www.attilan.com, Attilan Software Factory
  • Sunday, November 15, 2009 11:33 PM
     
     Answered
    Hi Richard,

    yes, it's the GridViewRowPresenter that causes the effect. That is, the Template then only considers GridViewRows.
    If you replace its definition with <ContentPresenter/> , you would see the items without having to add the GridView(Column), i.e. as in your first attempt to use my style.

    Cheers,
    Olaf
    • Marked As Answer by Richard Guion Monday, November 16, 2009 4:11 AM
    •  
  • Thursday, January 28, 2010 11:15 PM
     
     

    William,

    I'm using your example, but running into another problem.  It works perfectly, unless I define a ItemContainerStyle for the list view.  When I do that, the mouse over highlighting in the container style works, but the selected and selected (but not focused)  brushes are no red and yellow (as in my example).  Do you (or anyone else) have any ideas to resolve that issue?

    <Window.Resources>
      <Style x:Key="{x:Type ListViewItem}" TargetType="ListViewItem">
        <Style.Resources>
          <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red" />
          <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Yellow" />
        </Style.Resources>
      </Style>
    </Window.Resources>

    <StackPanel>
    <ListView>
      <ListView.ItemContainerStyle>
        <Style>
          <Setter Property="Control.Margin" Value="2" />
          <Style.Triggers>
            <Trigger Property="Control.IsMouseOver" Value="True">
              <Setter Property="Control.Background" Value="Green" />
            </Trigger>
          </Style.Triggers>
        </Style>
      </ListView.ItemContainerStyle>
    ...
    </ListView>
    </StackPanel>

  • Friday, January 29, 2010 8:28 AM
     
      Has Code
    Hi H,

    what happens is that your ItemContainerStyle effectively overwrites the previously defined ListViewItem style. You'll thus have to move the style into the ItemContainerStyle:

    <Window x:Class="WpfTests.ListView_SimpleSelectionStyle"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
       <StackPanel>
          <ListView>
             <ListView.ItemContainerStyle>
                <Style>
                   <Style.Resources>
                      <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red"/>
                      <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Yellow" />
                   </Style.Resources>
                   <Setter Property="Control.Margin" Value="2" />
                   <Style.Triggers>
                      <Trigger Property="Control.IsMouseOver" Value="True">
                         <Setter Property="Control.Background" Value="Green" />
                      </Trigger>
                   </Style.Triggers>
                </Style>
             </ListView.ItemContainerStyle>
             <ListViewItem>Item 1</ListViewItem>
             <ListViewItem>Item 2</ListViewItem>
             <ListViewItem>Item 3</ListViewItem>
          </ListView>
       </StackPanel>
    </Window>

    Cheers,
    Olaf

    Visit my blog @ http://blogs.intuidev.com
  • Wednesday, November 10, 2010 5:30 PM
     
      Has Code
    Olaf, thanks for helping me out.  I tried your ResourceDictionary, and I see the mouse over and selection gradient effects--beautiful!

    One problem I have, and maybe this is because I tested it with regular listview instead of a gridview.  I see the rows where the list view items are, but no text!  With no hover/selection, it's a grey row.  Even in hover/selected mode, I see the gradients, but no text.

    All I did was save your XAML resource dictionary, and referenced it from a little test app in Kaxaml.  Did I do something wrong or do I have to change a style for the foreground text?

    <Page
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
     
      <Page.Resources>
       <ResourceDictionary>
         <ResourceDictionary.MergedDictionaries>
          <ResourceDictionary Source="C:/Projects/samples/WPF/ListViewStyles/ListViewStyle.xaml" />
         </ResourceDictionary.MergedDictionaries>
       </ResourceDictionary>
      </Page.Resources>
     
     <Grid> 
     <ListView>
      <ListViewItem>Item 1</ListViewItem>  
      <ListViewItem>Item 2</ListViewItem>
      <ListViewItem>Item 3</ListViewItem>  
     </ListView> 
     </Grid>
    </Page>
    

    // Richard | www.attilan.com, Attilan Software Factory

    Hi,
    here my approach for defining a ListViewItem Style that can be used both in ListView and GridView:

    http://dotnetlearning.wordpress.com/2010/11/09/highlight-selected-item-listview-gridview-listviewitem/

    Regards,
    Marzio.