简体   繁体   English

WPF TabControl,用C#代码改变TabItem的背景色

[英]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.但所有这些都由 .xaml 回答。 However, what I need is the back code.但是,我需要的是后退代码。 I have a TabControl.我有一个 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.但是,目前,我只有一个 TabControl 实例和一个名为 ActiveTabIndex 的属性。

---------------------------------------Edit 1----------------------------------------------- -------------------------------编辑1--------- -------------------------------

I have added an event SelectionChanged我添加了一个事件 SelectionChanged

(this.myTabControl as System.Windows.Controls.TabControl).SelectionChanged += TabSet_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------------------------------ ---------------编辑2------------------------ -----

After a long long long discussion.经过长时间的讨论。 I've decided (actually not my decision) to use the XAML.我已经决定(实际上不是我的决定)使用 XAML。 Yeah.是的。 I am new to WPF.我是 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.问题在这里:当鼠标悬停在 TabItem 上时,我想将背景颜色更改为橙​​色。 Now the color is Orange when the mouse is over the ContentPanel and TabItem.现在,当鼠标悬停在 ContentPanel 和 TabItem 上时,颜色为橙色。 I need it to be orange when the mouse is over the TabItem only.当鼠标仅在 TabItem 上时,我需要它是橙色的。 (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 ---------------- ------------- 编辑 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 ------------------- ---------------编辑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.我用Snoop查看了一下,原来Background在本地设置为红色。 I am a little confusing here.我在这里有点困惑。 I've asked around, and someone gave me the suggestion to use TemplateBinding since it is under ControlTemplate.我四处询问,有人建议我使用 TemplateBinding,因为它在 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.但是我不明白为什么要使用 TemplateBinding,因为我读了一些文章说它用于编译时绑定。 I am totally confused.我完全糊涂了。 I am new to WPF, I am sorry if the question is low level question.我是 WPF 的新手,如果问题是低级问题,我很抱歉。 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! WPF 专门设计用于将视觉状态与代码分离,忽略这一点后果自负!

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.实际上,TabItem 有 7 种不同的视觉状态,添加代码来涵盖这些情况中的每一种都开始变得混乱,而且肯定比使用 XAML 复杂得多。

The "proper" way to control background color via code is with XAML and data binding.通过代码控制背景颜色的“正确”方法是使用 XAML 和数据绑定。 First of all go to theMicrosoft MSDN page for WPF TabControl Styles and Templates where you'll find the complete template for TabItem.首先转到WPF TabControl 样式和模板Microsoft MSDN 页面,您将在其中找到 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).将其粘贴到您的 Window.Resources 部分,现在您可以开始使用它的外观(不要忘记添加命名空间和资源)。 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.如果您使用各种资源,您会注意到 TabItems 使用线性渐变,ControlLightColor 用于所有选项卡的顶部,ControlMediumColor 用于未选定选项卡的底部,ControlDarkColor 用于当前选定选项卡的底部. 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.要在运行时控制这一点,您需要找到分散在样式中的这些资源的所有实例并将它们绑定到代码中的一个属性,然后编写一个 ValueConverter 将您的任何属性(我猜是布尔值)转换为刷。 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.另一种(更好的)方法,并且根本不需要编写任何额外代码,是使用 DataTrigger 来更改 TabItem 的视觉外观以响应您的属性更改值,但这有点过了“初学者”阶段,您需要提供有关您的具体案例的更多信息。

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.如果您绝对坚持在代码中执行此操作,那么您不应该使用 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:更新 #2:您就快到了,您只需要在承载选项卡控件的窗口中执行此操作:

<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. WPF 允许您基于现有控件创建新的自定义控件类型,然后您可以使用 Microsoft 站点上的模板/样式声明填写它并更改位以适合您。 Create a new user control called MyTabControl and replace the behind code with this:创建一个名为 MyTabControl 的新用户控件并将后面的代码替换为:

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):现在用这个替换 xaml(您需要将命名空间更改为您的项目的任何名称):

<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):现在在您的 MainWindow 或任何像常规 TabControl 一样使用它的东西,SelectedBgClr 将设置选定的选项卡标题和主面板背景(如果您查看上面的 XAML,您会看到两者的绑定):

<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.请注意,背后的代码是最少的,它仍然是 XAML 做大部分工作,而 MyTabControl 只是用作依赖属性的包装器。 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.在实际应用程序中,您将使用称为附加属性的东西,这样您就不需要派生一个全新的 TabControl 类。

Put this in your TabControlChanged event:把它放在你的TabControlChanged事件中:

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
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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