2010-03-26 15 views
10

Problema
Ho un controllo scheda personalizzata utilizzando le schede a forma di Chrome che si lega ad un ViewModel. A causa della forma, i bordi si sovrappongono un po '. Ho una funzione che imposta lo ZIndex di tabItem su TabControl_SelectionChanged che funziona bene per la selezione delle schede e il trascinamento/rilascio delle schede, tuttavia quando aggiungo o chiudi una scheda tramite un comando di inoltro ottengo risultati insoliti. Qualcuno ha qualche idea? guardaWPF - sovrapposte personalizzati schede in un TabControl e ZIndex

predefinita:

Rimozione schede:

aggiungendo 2 o più schede consecutive:

L'aggiunta di più di 1 scheda alla volta non reimposta lo zindex di altre schede aggiunte di recente, in modo che vadano dietro la scheda a destra, e le schede di chiusura non rendono correttamente lo ZIndex del SelectedTab che lo sostituisce e viene visualizzato dietro il scheda alla sua destra.

codice per impostare 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))); 
       } 
      } 
     } 
    } 

Utilizzando i punti di interruzione che posso vedere che sia correttamente l'impostazione del ZIndex a quello che io voglio che, tuttavia il layout è che non visualizza le modifiche. So che alcune modifiche sono effettive perché se nessuna di esse funzionasse, i bordi delle schede verrebbero invertiti (le schede corrette verrebbero disegnate sopra quelle a sinistra). Facendo clic su una scheda si imposta correttamente la zindice di tutte le schede (inclusa quella che dovrebbe essere disegnata in alto) e trascinandole/rilasciandole per riorganizzarle si esegue anche il rendering corretto (che rimuove e reinserisce l'elemento della scheda). L'unica differenza che posso pensare è che sto usando il modello di progettazione MVVM ei pulsanti che le schede Aggiungi/Chiudi sono comandi di inoltro.

Qualcuno ha qualche idea del perché questo sta accadendo e come posso risolvere il problema ??

p.s. Ho provato a impostare uno ZIndex nel mio ViewModel e ad associarlo, tuttavia la stessa cosa accade quando aggiungo/rimuovi le schede tramite il comando relay.

+0

Ora sto pensando che sia un problema con WPF e non il mio codice. Ho aggiunto un pulsante che visualizza zIndex degli elementi della scheda dopo che sono stati disegnati e zIndex è sicuramente corretto su tutti loro, non vengono disegnati correttamente. – Rachel

+0

Ciao. Dare tabs in un TabControl la forma trapezoidale simile a Chrome e far sì che si comportino correttamente sembra essere piuttosto complicato in WPF. Qualche possibilità di condividere lo XAML che hai usato per il template/styling? Ho una soluzione qui, ma non è particolarmente elegante - curioso di quello che hai fatto! Saluti. – Noldorin

+0

Certo, il codice è un po 'troppo lungo per postare qui, ma proverò a spiegarlo. In caso di domande, non esitate a scrivermi a [email protected] Ogni Tab è in realtà una Griglia con 3 celle: quelle Sinistra e Destra contengono un Percorso che disegna la curva e quello centrale contiene i dati. Ciascuna griglia ha un margine negativo impostato per farli sovrapporre e zIndex viene usato per impostare quale deve essere in cima. Non sono sicuro se stai aggiungendo il trascinamento/rilascio o lo scorrimento, ma nel mio caso tutte le schede sono contenute in un ScrollViewer per lo scorrimento e DragDrop è una proprietà associata a ItemsControl di TabControl. – Rachel

risposta

5

Grazie, Abe, il tuo secondo commento mi ha portato alla mia soluzione!

Ho aggiunto tabItem.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate); a ogni iterazione del ciclo.

Sarei ancora interessato ad apprendere se qualcun altro trova un modo per aggirare questo senza aggiornare ogni tabItem ad ogni cambiamento. Ho provato a rinfrescare l'intero controllo a schede alla fine del ciclo, ma questo ha funzionato solo per chiudere le schede, non aggiungendole. So che Panel.ZIndex è impostato correttamente, non sta onorando quella proprietà durante il rendering.

MODIFICA: la riga di codice precedente causava uno sfarfallio insolito durante il trascinamento/rilascio delle schede che mostravano brevemente la scheda dietro quella trascinata. Ho spostato il codice su una funzione separata e l'ho richiamato a una priorità di dispatcher inferiore e questo ha risolto il problema. Il codice finale è il seguente:

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

L'aggiornamento a .Net 4.0 ha risolto anche il problema – Rachel

1

Sembra che devi solo eseguire di nuovo l'algoritmo quando la raccolta cambia. Poiché si sta testando la proprietà ItemContainerGenerator.Status, l'algoritmo potrebbe non essere eseguito. Si consiglia di prendere in considerazione l'ascolto dell'evento StatusChanged e quando si passa a ContainersGenerated eseguire nuovamente l'algoritmo.

+0

Ho provato questo e ancora non viene visualizzato correttamente. Inoltre, l'evento ItemContainerGenerator.StatusChanged viene eseguito prima dell'aggiornamento di SelectedItem, pertanto l'impostazione imposta sempre la scheda selezionata sull'ultima scheda selezionata. Usando i breakpoints posso vedere che Panel.ZIndex è sicuramente impostato correttamente quando si aggiungono/rimuovono le schede usando l'evento TabControl_SelectionChanged, tuttavia non le disegna come dovrebbe. Per me questo non ha senso dato che le schede Drag/Dropping (che rimuove e riattiva anche il tabItem) disegna correttamente l'ordine di tabulazione. – Rachel

+2

Sì, è strano. L'unica differenza che posso immaginare tra d & d e l'aggiunta di un nuovo elemento sarebbe il momento in cui verrà eseguito l'algoritmo z-index. Forse invocarlo sul Dispatcher con una priorità più bassa (come DispatcherPriority.Input) sarebbe una soluzione (hacky). –

Problemi correlati