ListBox mouse over background color

只谈情不闲聊 提交于 2019-12-03 22:44:29

问题


The problem I have is the MouseOver trigger to color the background fails on the selected row.
For any non-selected row the background turns blue on mouse over.
But no blue background for the selected row.
Click on a row and then the background blue goes away.

I also tried the style in the ListBox.ItemContainerStyle

<Window x:Class="ListBoxLastIntoView.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 Height="0"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <ListBox Grid.Row="1" Grid.ColumnSpan="2" x:Name="lvMVitems" 
            ItemsSource="{Binding Mode=OneWay}" 
            ScrollViewer.CanContentScroll="False" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" MaxHeight="300">
            <ListBox.Resources>
                <Style TargetType="ListBoxItem">
                    <Style.Resources>
                        <!-- Background of selected item when focussed -->
                        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
                        <!-- Background of selected item when not focussed -->
                        <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="Transparent" />
                    </Style.Resources>
                    <Style.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Background" Value="LightSteelBlue" />
                        </Trigger>
                    </Style.Triggers>
                </Style>                 
            </ListBox.Resources>
            <!--<ListBox.ItemContainerStyle>               
            </ListBox.ItemContainerStyle>-->
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{TemplateBinding Content}" Background="Orange" Margin="20,2,2,2">
                    </TextBlock>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>


namespace ListBoxLastIntoView
{
    public partial class MainWindow : Window
    {
        private string lorum = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
        private List<string> lorums = new List<string>();
        public MainWindow()
        {
            for (int i = 1; i < 100; i++) lorums.Add(i.ToString() + " " + lorum);           
            InitializeComponent();
            //lb.ItemsSource = lorums;
            lvMVitems.ItemsSource = lorums;
        }
    }
}

回答1:


If you look at default template of ListBoxItem, you will see IsMouseOver trigger is applied before IsSelected trigger and since dataTriggers are evaluated from top to bottom. So, last trigger always won and set background of ListBoxItem as Transparent (in your case).

In case you want to override that behaviour, you have to override default template and set your values over there and change ordering of triggers. Below is the sample how to do it, you can change background colors and border brushes as per your needs:

<Style TargetType="ListBoxItem">
  <Setter Property="Template">
     <Setter.Value>
       <ControlTemplate TargetType="ListBoxItem">
          <Border BorderThickness="{TemplateBinding Border.BorderThickness}"
                  Padding="{TemplateBinding Control.Padding}"
                  BorderBrush="{TemplateBinding Border.BorderBrush}"
                  Background="{TemplateBinding Panel.Background}"
                  Name="Bd"
                  SnapsToDevicePixels="True">
             <ContentPresenter Content="{TemplateBinding ContentControl.Content}"
                               ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
                               ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}"
                               HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
                               VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
                               SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
          </Border>
          <ControlTemplate.Triggers>
             <MultiTrigger>
                <MultiTrigger.Conditions>
                  <Condition Property="Selector.IsSelectionActive" Value="False"/>
                  <Condition Property="Selector.IsSelected" Value="True"/>
                </MultiTrigger.Conditions>
                <Setter Property="Panel.Background" TargetName="Bd" 
                        Value="Transparent"/>
                <Setter Property="Border.BorderBrush" TargetName="Bd">
                   <Setter.Value>
                     <SolidColorBrush>#FFDADADA</SolidColorBrush>
                   </Setter.Value>
                </Setter>
             </MultiTrigger>
             <MultiTrigger>
               <MultiTrigger.Conditions>
                 <Condition Property="Selector.IsSelectionActive" Value="True"/>
                 <Condition Property="Selector.IsSelected" Value="True"/>
               </MultiTrigger.Conditions>
               <Setter Property="Panel.Background" TargetName="Bd" Value="Transparent"/>
               <Setter Property="Border.BorderBrush" TargetName="Bd">
                 <Setter.Value>
                   <SolidColorBrush>#FF26A0DA</SolidColorBrush>
                 </Setter.Value>
               </Setter>
             </MultiTrigger>
             <Trigger Property="UIElement.IsMouseOver" Value="True">
               <Setter Property="Panel.Background" TargetName="Bd" 
                       Value="LightSteelBlue"/>
               <Setter Property="Border.BorderBrush" TargetName="Bd">
                 <Setter.Value>
                   <SolidColorBrush>#A826A0DA</SolidColorBrush>
                 </Setter.Value>
               </Setter>
             </Trigger>
             <Trigger Property="UIElement.IsEnabled" Value="False">
               <Setter Property="TextElement.Foreground" TargetName="Bd">
                 <Setter.Value>
                   <DynamicResource ResourceKey="{x:Static SystemColors.GrayTextBrushKey}" />
                 </Setter.Value>
               </Setter>
             </Trigger>
          </ControlTemplate.Triggers>
       </ControlTemplate>
     </Setter.Value>
   </Setter>
</Style>

UPDATE

May be I should explain a bit more about my assertions above.

First of all, I had Windows 8 at my disposal and default templates are quite different for Windows 7 and Windows 8. For Windows 8, theme styles are picked from PresentationFramework.Aero2.dll, hence defualt templates might differ from theme style found under PresentationFramework.Aero.dll (used for Windows 7 and earlier versions).

I have posted default template I got from Aero2.dll. As you can see in default template, it doesn't use HighlightBrushKey so the question you posted where you are overriding HighlightBrushKey doesn't work at first place. (That's why I don't like overriding System brushes since it might not work for templates using another Theme style).

As per Dev's answer

Style triggers take precedence over template triggers anyways so it wouldn't matter if the template trigger is at first or last place in list.

I agree that Style triggers take precedence over template triggers. But, what i mentioned in my answer was the case when you haven't used any Style triggers and simply provide values in template. In default template MouseOverTrigger is at top and SelectedItem trigger below that. So, in that case order does matter. You can verify that your self by moving up the trigger and removing all Style triggers.

And regarding

I am not sure if the style Rohit posted to you is a complete copy of the ListBoxItem style. There seem to be style setters missing. He just posted you the template I guess.

I have posted the complete template of ListBoxItem found under Aero2.dll which obviously might differ based on theme style.

I got the style using XamlWriter.Save(listBoxItem.Template, xmlwrite);




回答2:


You have an iteressting issue there. I had it too awhile ago when writing a theme.

The problem is not order of triggers or which trigger is at bottom and which not.

Style triggers take precedence over template triggers anyways so it wouldn't matter if the template trigger is at first or last place in list.

The problem you have is very rare in wpf. In your case the implict style's trigger is simply ignored because background of border is not matched to background of the templated parent.

In your case a trigger is being called internally by microsoft when is selected property is true. Inside the trigger sets the background color directly on border element with the name "Bd" instead of setting the background on templated parent.

The result is there are two different backgrounds.

To make all that easy. You want to make following code run:

<Style TargetType="ListBoxItem">
   <Style.Triggers>
       <Trigger Property="IsMouseOver" Value="True">
           <Setter Property="Background" Value="LightSteelBlue" />
       </Trigger>
   </Style.Triggers>
</Style>

Internally this is what happens in microsoft's style.

 <MultiTrigger>
   <MultiTrigger.Conditions>
     <Condition Property="Selector.IsSelectionActive" Value="True"/>
     <Condition Property="Selector.IsSelected" Value="True"/>
   </MultiTrigger.Conditions>
   <Setter Property="Background" TargetName="Bd" Value="Blue"/>
 </MultiTrigger>

Border "Bd" background gets a new value but your style's mouse over trigger mains templated parent's background. Therefore you end up having two different backgrounds.

This is right approach microsoft should have taken!

 <MultiTrigger>
   <MultiTrigger.Conditions>
     <Condition Property="Selector.IsSelectionActive" Value="True"/>
     <Condition Property="Selector.IsSelected" Value="True"/>
   </MultiTrigger.Conditions>
   <Setter Property="Background" Value="Blue"/>
 </MultiTrigger>

Its the line <Setter Property="Background" Value="Blue"/> that makes the big change.

With the approach I showed you you wouldn't end up having two different backgrounds.

Now that you know what is going wrong internally lets go to the most important part how to solve this issue.

The solution is... and at this point me and Rohit agree on same issue solving approach... unfortunately you will have to rewrite the style or copy past it and match backgrounds so there won't be standalone values for border element. Border element should listen to templated parent's background.

The best way to see the current default template is to use Blend and to pick the option to create copy of contro's template for edit.

There are also default templates of wpf controls somewhere on the internt. You could find the ListBoxItem style and copy past it before adding your edits to it.

I am not sure if the style Rohit posted to you is a complete copy of the ListBoxItem style. There seem to be style setters missing. He just posted you the template I guess.



来源:https://stackoverflow.com/questions/21972818/listbox-mouse-over-background-color

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!