WPF-TabControl和ZIndex中重叠的自定义选项卡

WPF-TabControl和ZIndex中重叠的自定义选项卡,wpf,tabcontrol,z-index,tabitem,Wpf,Tabcontrol,Z Index,Tabitem,问题 我有一个自定义选项卡控件,它使用绑定到ViewModel的镀铬选项卡。由于形状的原因,边缘有点重叠。我有一个函数,可以在TabControl\u SelectionChanged上设置tabItem的ZIndex,该函数可以很好地用于选择选项卡和拖放选项卡,但是当我通过中继命令添加或关闭选项卡时,我会得到不寻常的结果。有人有什么想法吗 默认视图: 删除选项卡: 在行中添加两个或更多选项卡: 一次添加多个选项卡不会重置其他最近添加的选项卡的zindex,因此它们会位于右侧选项卡的后面,

问题
我有一个自定义选项卡控件,它使用绑定到ViewModel的镀铬选项卡。由于形状的原因,边缘有点重叠。我有一个函数,可以在
TabControl\u SelectionChanged
上设置tabItem的ZIndex,该函数可以很好地用于选择选项卡和拖放选项卡,但是当我通过中继命令添加或关闭选项卡时,我会得到不寻常的结果。有人有什么想法吗

默认视图:

删除选项卡:

在行中添加两个或更多选项卡:

一次添加多个选项卡不会重置其他最近添加的选项卡的zindex,因此它们会位于右侧选项卡的后面,关闭选项卡不会正确呈现替换它的选定选项卡的zindex,它会显示在右侧选项卡的后面

设置ZIndex的代码

通过使用断点,我可以看到它正确地将ZIndex设置为我想要的,但是布局没有显示更改。我知道有些更改是有效的,因为如果它们都不起作用,则选项卡边缘将反转(右选项卡将绘制在左选项卡的顶部)。单击某个选项卡将正确设置所有选项卡(包括应在顶部绘制的选项卡)的zindex,并通过拖放这些选项卡来重新排列它们,从而正确渲染(这将删除并重新插入选项卡项)。我能想到的唯一区别是我使用的是MVVM设计模式,添加/关闭选项卡的按钮是中继命令

有没有人知道为什么会发生这种情况,以及我如何解决它


p、 我确实尝试在我的ViewModel中设置一个ZIndex并绑定到它,但是当通过relay命令添加/删除选项卡时也会发生同样的情况。

听起来,当集合发生变化时,您只需要再次运行算法。由于您正在测试
ItemContainerGenerator.Status
属性,因此算法可能无法运行。你可能想考虑听<代码> StasuSuffs事件,当它改变为<代码>容器生成的< /代码>再次运行算法。

< P>谢谢Abe,你的第二个评论引导我找到了解决方案!p> 我添加了
tabItem.Dispatcher.Invoke(DispatcherPriority.Render,EmptyDelegate)到循环的每个迭代

我仍然有兴趣了解是否有其他人找到了解决这个问题的方法,而不必在每次更改时刷新每个选项卡项。我尝试在循环结束时刷新整个选项卡控件,但这只适用于关闭选项卡,而不用于添加它们。我知道Panel.ZIndex设置正确,只是渲染时不尊重该属性

编辑:当拖放选项卡时,上面的代码行会导致不寻常的闪烁,这将短暂显示正在拖动的选项卡后面的选项卡。我将代码移动到一个单独的函数,并以较低的调度程序优先级调用它,这就解决了这个问题。最终代码如下:

private void PrimaryTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.Source is TabControl)
        {
            TabControl tabControl = sender as TabControl;

            tabControl.Dispatcher.BeginInvoke(
                new Action(() => UpdateZIndex(sender as TabControl)),
                DispatcherPriority.Background);
        }
    }

    private void UpdateZIndex(TabControl tabControl)
    {
        ItemContainerGenerator icg = tabControl.ItemContainerGenerator;

        if (icg.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
        {
            foreach (object o in tabControl.Items)
            {
                UIElement tabItem = icg.ContainerFromItem(o) as UIElement;
                if (tabItem != null)
                {
                    // Set ZIndex
                    Panel.SetZIndex(tabItem, (o == tabControl.SelectedItem ? 100 :
                        90 - tabControl.Items.IndexOf(o)));
                }
            }
        }
    }

我已经试过了,但仍然没有正确显示。此外,ItemContainerGenerator.StatusChanged事件在更新SelectedItem之前运行,因此设置它时始终将选定选项卡设置为最后选定的选项卡。通过使用断点,我可以看到Panel.ZIndex在使用TabControl_SelectionChanged事件添加/删除选项卡时确实设置正确,但是它没有按应有的方式绘制它们。对我来说,这是没有意义的,因为拖放标签(同时删除和重新添加tabItem)正确地绘制了标签顺序。是的,这很奇怪。我能想到的d&d和仅仅添加一个新项之间的唯一区别是z-index算法运行的时间。也许在调度器上以较低的优先级调用它(如DispatcherPriority.Input)将是一个(hacky)解决方案。我现在认为这是WPF的问题,而不是我的代码的问题。我添加了一个按钮,显示了选项卡项在绘制后的zIndex,所有选项卡项的zIndex都是正确的,只是没有正确绘制。您好。在WPF中,给TabControl中的tab赋予类似于铬的梯形形状,并让它们正确地工作似乎是相当棘手的。您是否有机会分享用于模板/样式设置的XAML?我这里有一个解决方案,但它不是特别优雅-好奇你做了什么!干杯。当然,代码有点太长,不能在这里发布,但我会尽力解释。如果您有任何问题,请随时发电子邮件给我rlim@email.itt-技术教育。每个选项卡实际上是一个包含3个单元格的网格-左侧和右侧的单元格包含绘制曲线的路径,中间的单元格包含数据。每个网格都有一个负边距集以使它们重叠,zIndex用于设置哪个网格应该位于顶部。不确定是否要添加拖放或滚动,但在我的例子中,所有选项卡都包含在滚动查看器中,并且DragDrop是TabControl的ItemsControl的附加属性。@Rachel,除了TabPanel之外,不能创建一个面板来反向重新排序它的子对象,并给他们想要的Z顺序吗?升级到.Net 4.0也解决了这个问题
private void PrimaryTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.Source is TabControl)
        {
            TabControl tabControl = sender as TabControl;

            tabControl.Dispatcher.BeginInvoke(
                new Action(() => UpdateZIndex(sender as TabControl)),
                DispatcherPriority.Background);
        }
    }

    private void UpdateZIndex(TabControl tabControl)
    {
        ItemContainerGenerator icg = tabControl.ItemContainerGenerator;

        if (icg.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
        {
            foreach (object o in tabControl.Items)
            {
                UIElement tabItem = icg.ContainerFromItem(o) as UIElement;
                if (tabItem != null)
                {
                    // Set ZIndex
                    Panel.SetZIndex(tabItem, (o == tabControl.SelectedItem ? 100 :
                        90 - tabControl.Items.IndexOf(o)));
                }
            }
        }
    }