简体   繁体   中英

WPF TabControl, change the background color of the TabItem with C# codes

Hi I think this is a beginners questions. I've searched all the related questions. But all of them are answered by .xaml. However, what I need is the back code. I have a TabControl. I need to set the background color of its items. I need to set different colors for the items when it is selected, unselected and hover. Thank you so much for your help. I want to post my codes here. However, currently, all I have is an instance of the TabControl and one property called ActiveTabIndex.

---------------------------------------Edit 1-----------------------------------------------

I have added an event SelectionChanged

(this.myTabControl as System.Windows.Controls.TabControl).SelectionChanged += TabSet_SelectionChanged;

void TabSet_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
        {
            foreach (System.Windows.Controls.TabItem item in (this.myTabControl as System.Windows.Controls.TabControl).Items)
            {
                if (item == (this.myTabControl as System.Windows.Controls.TabControl).SelectedItem)
                {
                    item.Background = System.Windows.Media.Brushes.Red;
                }
                else
                    item.Background = System.Windows.Media.Brushes.Green;
            }
        }

However, I can only set the Green actually. The background color of the selected item keeps as the default color instead of red. Any hints about this? Also, I would like to know how to add event for the hover. Haven't find the exact event. Thanks again.

-----------------------Edit 2------------------------------

After a long long long discussion. I've decided (actually not my decision) to use the XAML. Yeah. I am new to WPF. So I still have questions about this (I am so sorry for this, Please bear me). The questions are here: I would like to change the background color to Orange when the mouse is over the TabItem. Now the color is Orange when the mouse is over the ContentPanel and TabItem. I need it to be orange when the mouse is over the TabItem only. (I am not sure I am clear enough.) Another question is that I would let users to set the color instead of setting is to red directly. I need some bindings I think. For this question, I will google later for sure. Just want to be clear. Thank you so much for all of you. Really helpful.

<TabItem x:Class="MyControls.Tab"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <TabItem.Style>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TabItem}">
                        <Grid>
                            <Border  Name="Border" Margin="0,0,-4,0" BorderThickness="1,1,1,1" CornerRadius="2,12,0,0" >
                                <ContentPresenter x:Name="ContentSite" VerticalAlignment="Center" HorizontalAlignment="Center" ContentSource="Header" Margin="12,2,12,2" RecognizesAccessKey="True"/>
                            </Border>
                        </Grid>
                        <ControlTemplate.Triggers>

                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="Panel.ZIndex" Value="100" />
                                <Setter TargetName="Border" Property="Background" Value="Red" />
                                <Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
                            </Trigger>

                            <Trigger Property="IsSelected" Value="False">
                                <Setter TargetName="Border" Property="Background" Value="Green" />                                
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="Border" Property="Background" Value="Orange" />
                            </Trigger>

                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </TabItem.Style>
</TabItem>

------------- Edit 3 ----------------

I am not clear enough I think. Here is what it is now: It's working fine if the mouse is over other tabs:在此处输入图片说明

But When the mouse is over the circled area, the background color of the selected item should be red instead of orange:在此处输入图片说明

---------------Edit 4 -------------------

Thanks for all of you for your reply. Now after a long discussion with my users and some others, we would like to change the background color dynamically. The user wants to set the color by themselves. Basically, I need first do some binding (if this is the term). I've tried the following. However, the selected tab is not with red background. I used the Snoop to check out, it turns out that the Background is set as red locally. I am a little confusing here. I've asked around, and someone gave me the suggestion to use TemplateBinding since it is under ControlTemplate. But, I've tried to create dependency property and something like that. But just I don't understand why should I use TemplateBinding since I read some article said that it is for compile time binding. I am totally confused. I am new to WPF, I am sorry if the question is low level question. Thanks again!

<TabItem x:Class="MyControl.Tab"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <TabItem.Style>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TabItem}">
                        <Grid>
                            <Border  Name="Border" Margin="0,0,-4,0" BorderThickness="1,1,1,1" CornerRadius="2,12,0,0" >
                                <ContentPresenter x:Name="ContentSite" VerticalAlignment="Center" HorizontalAlignment="Center" ContentSource="Header" Margin="12,2,12,2" RecognizesAccessKey="True"/>
                            </Border>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="Panel.ZIndex" Value="100" />
                                <Setter TargetName="Border" Property="Background" Value="{Binding SelectedBgClr}" />
                                <Setter Property="Foreground" Value="Yellow" />
                                <Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
                            </Trigger>                            
                            <Trigger Property="IsSelected" Value="False">
                                <Setter TargetName="Border" Property="Background" Value="Green" /> 
                                <Setter Property="Foreground" Value="AliceBlue"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="Border" Property="Background" Value="Orange" />
                                <Setter Property="Foreground" Value="Black"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </TabItem.Style>
</TabItem>

The behind code is:

 public Tab()
        {
            SelectedBgClr = new SolidColorBrush(Colors.Red);
            //UnSelectedBgClr = new SolidColorBrush(Colors.Green);
            //HoverBgClr = new SolidColorBrush(Colors.Orange);
            InitializeComponent();

        }
public static readonly DependencyProperty SelectedBgClrProperty = DependencyProperty.Register("SelectedBgClr", typeof(Brush), typeof(Tab), new UIPropertyMetadata(null));
public Brush SelectedBgClr
{
    get
    {
        return (Brush)GetValue(SelectedBgClrProperty);
    }
    set
    {
        SetValue(SelectedBgClrProperty, value);
    }
}

The reason you're finding it difficult to get answers to your question is because you're going about it completely the wrong way; I can think of very few cases where changing controls in code as you're suggesting would be justified. WPF has been specifically designed to decouple visual state from code, ignore this at your own peril!

To actually answer your question though the following will do the trick...sort of...

foreach (TabItem item in tabControl.Items)
    item.Background = new SolidColorBrush(Colors.Blue);

If you do this then you'll notice it changes the background color of unselected tabs but not the currently selected tab. You'll also notice that highlighting a tab with the mouse will display another color yet again. There are in fact 7 different visual states for TabItem, adding code to cover each of these cases starts to get messy and certainly a lot more complex than using XAML.

The "proper" way to control background color via code is with XAML and data binding. First of all go to theMicrosoft MSDN page for WPF TabControl Styles and Templates where you'll find the complete template for TabItem. Paste that into your Window.Resources section and now you can start playing around with the way it looks (don't forget to the add the namespaces and resources as well). If you play around with the various resources you'll notice that TabItems use a linear gradient, ControlLightColor is used for the top of all tabs, ControlMediumColor is used for the bottom of unselected tabs and ControlDarkColor is used for the bottom of the currently selected tab. To control this at run time you need to find all the instances of these resources scattered within the style and bind them to a property in your code, then write a ValueConverter that converts whatever your property is (I'm guessing a boolean) to a brush. Another (better) method, and one that doesn't require any extra code to be written at all, would be to use a DataTrigger that changes the visual appearence of the TabItem in response to your property changing value, but that's getting a little bit past the "beginner" stage and you'd need to provide more info on your specific case.

UPDATE: This seems to work for me:

void TabSet_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
    {
        foreach (TabItem item in tabControl.Items)
            item.Background = new SolidColorBrush(item.IsSelected ? Colors.Green : Colors.Red);
    }

I still say this is horribly wrong though. If you absolutely insist on doing this in code then you shouldn't be using WPF. It's completely the wrong technology, I cannot stress this strongly enough!

UPDATE #2: You're almost there, you just need to do this in the window that hosts the tab control:

<Window x:Class="MyWpfApplication.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" WindowState="Maximized">

    <Window.Resources>

        <Style TargetType="{x:Type TabItem}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TabItem}">
                        <Grid>
                            <Border  Name="Border" Margin="0,0,-4,0" BorderThickness="1,1,1,1" CornerRadius="2,12,0,0" >
                                <ContentPresenter x:Name="ContentSite" VerticalAlignment="Center" HorizontalAlignment="Center" ContentSource="Header" Margin="12,2,12,2" RecognizesAccessKey="True"/>
                            </Border>
                        </Grid>
                        <ControlTemplate.Triggers>

                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="Panel.ZIndex" Value="100" />
                                <Setter TargetName="Border" Property="Background" Value="Red" />
                                <Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
                            </Trigger>

                            <Trigger Property="IsSelected" Value="False">
                                <Setter TargetName="Border" Property="Background" Value="Green" />
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="Border" Property="Background" Value="Orange" />
                            </Trigger>

                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </Window.Resources>

    <TabControl>
        <TabItem Header="Foo" />
        <TabItem Header="Bar" />
        <TabItem Header="Baz" />
    </TabControl>

</Window>

WPF lets you create a new custom control type based on an existing control, you can then fill it out with the template/style declaration on the Microsoft site and change the bits to suit you. Create a new user control called MyTabControl and replace the behind code with this:

public partial class MyTabControl : TabControl
{
    public static readonly DependencyProperty SelectedBgClrProperty = DependencyProperty.Register("SelectedBgClr",
        typeof(Brush), typeof(MyTabControl), new UIPropertyMetadata(null));

    [Category("Appearance")]
    public Brush SelectedBgClr
    {
        get
        {
            return (Brush)GetValue(SelectedBgClrProperty);
        }
        set
        {
            SetValue(SelectedBgClrProperty, value);
        }
    }

    public MyTabControl()
    {
        InitializeComponent();
    }
}

Now replace the xaml with this (you'll need to change the namespace to whatever your project is):

<TabControl x:Class="TabDemo.MyTabControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         Name="tabControl"
         d:DesignHeight="300" d:DesignWidth="300">

<TabControl.Resources>

    <Style TargetType="{x:Type TabItem}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TabItem}">
                    <Grid>
                        <Border  Name="Border" Margin="0,0,-4,0" BorderThickness="1,1,1,1" CornerRadius="2,12,0,0" >
                            <ContentPresenter x:Name="ContentSite" VerticalAlignment="Center" HorizontalAlignment="Center" ContentSource="Header" Margin="12,2,12,2" RecognizesAccessKey="True"/>
                        </Border>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Panel.ZIndex" Value="100" />
                            <Setter TargetName="Border" Property="Background" Value="{Binding ElementName=tabControl, Path=SelectedBgClr}" />
                            <Setter Property="Foreground" Value="Yellow" />
                            <Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
                        </Trigger>
                        <Trigger Property="IsSelected" Value="False">
                            <Setter TargetName="Border" Property="Background" Value="Green" />
                            <Setter Property="Foreground" Value="AliceBlue"/>
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Border" Property="Background" Value="Orange" />
                            <Setter Property="Foreground" Value="Black"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</TabControl.Resources>

<TabControl.Style>
    <Style  TargetType="{x:Type TabControl}">
        <Setter Property="OverridesDefaultStyle"
          Value="True" />
        <Setter Property="SnapsToDevicePixels"
          Value="True" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TabControl}">
                    <Grid KeyboardNavigation.TabNavigation="Local">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
                                                Storyboard.TargetProperty="(Border.BorderBrush).
                    (SolidColorBrush.Color)">
                                            <EasingColorKeyFrame KeyTime="0"
                                         Value="#FFAAAAAA" />
                                        </ColorAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <TabPanel x:Name="HeaderPanel"
                    Grid.Row="0"
                    Panel.ZIndex="1"
                    Margin="0,0,4,-1"
                    IsItemsHost="True"
                    KeyboardNavigation.TabIndex="1" />
                        <Border x:Name="Border"
                  Grid.Row="1"
                  BorderThickness="1"
                  CornerRadius="2"
                  KeyboardNavigation.TabNavigation="Local"
                  KeyboardNavigation.DirectionalNavigation="Contained"
                  KeyboardNavigation.TabIndex="2" Background="{Binding ElementName=tabControl, Path=SelectedBgClr}">
                            <ContentPresenter x:Name="PART_SelectedContentHost"
                              Margin="4"
                              ContentSource="SelectedContent" />
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</TabControl.Style>

Now in your MainWindow or whatever just use it as you would a regular TabControl, the SelectedBgClr will set both the selected tab header and the main panel background (if you look in the XAML above you've see bindings for both):

<local:MyTabControl SelectedBgClr="Red">
        <TabItem Header="Foo"  />
        <TabItem Header="Bar" />
        <TabItem Header="Baz" />
    </local:MyTabControl>

Notice that the behind-code is minimal, it's still XAML that's doing the bulk of the work and MyTabControl is simply used as a wrapper for the dependency property. In a real application you would use something called an attached property so that you wouldn't need to derive a whole new TabControl class.

Put this in your TabControlChanged event:

foreach (TabItem AllTabItems in MyTabControl.Items) // Change MyTabControl with your TabControl name
{
    if (!AllTabItems.IsSelected)
    {
        // do something for all unselected tabitems
    }
    else if (AllTabItems.IsSelected)
    {
       // do something for the selected tabitem
    }
    else if (AllTabItems.IsMouseOver)
    {
       // do something for mouseover tabitem
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM