2010-03-26 42 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