2010-03-26 77 views
10

问题
我有使用Chrome形凸缘结合一个ViewModel自定义标签控制。由于形状,边缘重叠了一点。我有一个函数,可以在TabControl_SelectionChanged上设置tabItem的ZIndex,这对选择标签以及拖放标签非常有效,但是当我通过中继命令添加或关闭标签时,我收到了不寻常的结果。有没有人有任何想法?WPF - 重叠自定义标签在一个TabControl和Z-索引

默认视图:

卸下标签:

连续添加2周或更多的选项卡:

一次添加多于1个选项卡将不会重置其他最近添加的选项卡的zindex,以便它们位于右侧的选项卡后面,并且关闭选项卡不会正确地呈现SelectedTab的ZIndex,以替换它,并显示在在其右侧的标签。

代码来设置zIndex的

private void PrimaryTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     if (e.Source is TabControl) 
     { 
      TabControl tabControl = sender as 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; 
        Panel.SetZIndex(tabItem, (o == tabControl.SelectedItem ? 100 : 
         90 - tabControl.Items.IndexOf(o))); 
       } 
      } 
     } 
    } 

通过使用断点,我可以看到它是正确设置zIndex的什么,我想它,但布局不显示的变化。我知道一些变化是有效的,因为如果它们中的任何一个都没有工作,那么标签边缘将被颠倒(正确的标签将被绘制在左边的标签上)。单击一个选项卡将正确设置所有选项卡(包括应该绘制在顶部)的zindex,并拖放它们以重新排列它们也会正确呈现(这将删除并重新插入选项卡项)。我能想到的唯一区别是我使用的是MVVM设计模式,而Add/Close选项卡的按钮是中继命令。

有没有人有任何想法为什么发生这种情况,我该如何解决?

p.s.我曾尝试在我的ViewModel中设置ZIndex并绑定到它,但是通过relay命令添加/删除标签时会发生同样的情况。

+0

我现在认为这是WPF的问题,而不是我的代码。我添加了一个按钮,它们在绘制之后显示标签项目的zIndex,并且zIndex在所有这些项目上都是正确的,但它们只是没有正确绘制。 – Rachel 2010-03-29 14:14:24

+0

你好。在TabControl中为制表符提供类似Chrome的梯形形状并使它们的行为正确,但在WPF中确实似乎非常棘手。你有可能分享你用于模板/造型的XAML吗?我在这里有一个解决方案,但它不是特别优雅 - 好奇你做了什么!干杯。 – Noldorin 2010-04-22 23:42:17

+0

当然,代码有点太长,不能在这里发布,但我会尽力解释它。如果您有任何问题,请随时通过[email protected]给我发电子邮件。每个Tab实际上是一个带有3个单元格的网格 - Left和Right包含一个绘制曲线的路径,而中间的一个包含数据。每个Grid都有一个负边距设置,以使它们重叠,并且zIndex用于设置哪一个应该在最上面。不知道您是否添加拖放或滚动,但在我的情况下,所有Tabs都包含在ScrollViewer中进行滚动,并且DragDrop是TabControl的ItemsControl的附加属性。 – Rachel 2010-04-27 17:32:47

回答

5

谢谢安倍,你的第二个评论把我引向我的解决方案!

我将tabItem.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);添加到循环的每个迭代中。

我仍然有兴趣了解其他人是否找到解决方法,而无需在每次更改时刷新每个tabItem。我尝试在循环结束时刷新整个选项卡控件,但只适用于关闭选项卡,而不是添加它们。我知道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))); 
       } 
      } 
     } 
    } 
+1

升级到.Net 4.0也解决了这个问题 – Rachel 2010-05-20 13:54:41

1

听起来好像你只需要在集合更改时再次运行算法。由于您正在测试ItemContainerGenerator.Status属性,因此该算法可能无法运行。您可能需要考虑收听StatusChanged事件,并且当它更改为ContainersGenerated时,请再次运行该算法。

+0

我试过这个,但它仍然没有正确显示。此外,ItemContainerGenerator.StatusChanged事件在SelectedItem更新之前运行,因此设置它始终将选定选项卡设置为最后选定的选项卡。通过使用断点,我可以看到,使用TabControl_SelectionChanged事件添加/删除选项卡时,Panel.ZIndex肯定会正确设置,但它不会以应有的方式绘制它们。对我来说这是没有意义的,因为拖/放标签(它也删除并重新添加tabItem)正确地绘制标签顺序。 – Rachel 2010-03-29 14:02:53

+2

是的,这很奇怪。唯一的区别是我可以在d&d和添加新项目之间考虑的是z-index算法运行的时间。也许在Dispatcher上以较低优先级调用它(如DispatcherPriority.Input)将是一个(哈克)解决方案。 – 2010-03-30 17:16:08