2011-08-21 12 views
5

Sto sviluppando un'applicazione opensource denominata Media Assistant. Ho usato un ListBox per mostrare la libreria. ItemsSource è associato a un elenco di LibraryItem. Ecco lo XALM.Il ListBox WPF scorre verso l'alto quando cambio messaggio di stato o mostra schermata di attesa

<ListBox Name="Tree" DockPanel.Dock="Top" 
    ItemsSource="{Binding DataSource.OrderedLibraryItems}" 
    Background="{StaticResource LibraryBackground}" 
    Width="220" HorizontalAlignment="Left" 
    BorderThickness="0" 
    VirtualizingStackPanel.IsVirtualizing="True" 
    VirtualizingStackPanel.VirtualizationMode="Standard" 
    ScrollViewer.IsDeferredScrollingEnabled="True" 
    ItemTemplate="{StaticResource ListLibraryItemTemplate}" 
    SelectionMode="Single" 
    MouseDoubleClick="HandleMouseDoubleClick" 
/> 

Il problema si verifica quando viene visualizzato un messaggio di stato nella parte inferiore della finestra da un thread utilizzando Dispatcher.

Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,new ParameterizedThreadStart(action), state); 

Il ListBox scorre verso l'alto. Se non mostro alcun messaggio di stato, allora funziona bene. Il datacontext o gli elementi dell'elenco o lo stato attivo non sono stati modificati. Non ho trovato alcuna ragione per cui lo stia facendo. Succede quando visualizzo una schermata di attesa che è una finestra non modale. Non potrei ricrearlo in un progetto diverso. Here è il codice sorgente di Media Assistant. È possibile ricrearlo facilmente annullando il commento dell'istruzione return del metodo SetStatusMessage alla classe BackgroundScanner.

+0

ho avuto qualcosa di simile e tutto quello che posso immaginare è l'interfaccia utente ha voluto l'elemento selezionato in vista. Se l'indice era -1, allora provò a mostrare 0. Non stavo usando un messaggio di Dispatcher ma se metto il TextBlock sopra il ListBox ho avuto più fortuna ma non l'ho mai fatto funzionare fino in fondo. Con un controllo ListView ho ottenuto un comportamento migliore. ListBox è veloce e mi piace molto ma fa cose che non ho capito. Senza lo scrolling differito ottieni lo stesso comportamento? Se la tua modalità di rilegatura è su strada, dichiarala. – Paparazzi

risposta

3

Ho trovato la ragione di questo, quindi la soluzione. Ho usato un DockPanel per impaginare la mia interfaccia utente. Metto la mia barra di stato in basso, il ListBox a sinistra e gli altri elementi sono al centro e in alto. C'è un TextBlock nel mio StatusBar che ha larghezza e altezza impostato su Auto. Quindi, quando ho cambiato il testo del mio StatusBar TextBlock, la larghezza e l'altezza vengono ricalcolate e il genitore ricalcola il suo layout. Quindi il ListBox viene invocato su Measures and Arrange. Anche se la sua dimensione non viene modificata, si ripristina la sua posizione di scorrimento verso l'alto. Succede solo se utilizzo ScrollViewer.CanContentScroll = "True" al ListBox. Di default è True. Quindi, anche se non ho impostato questo valore, stava ripristinando la posizione di scorrimento. Se lo disabilito usando ScrollViewer.CanContentScroll = "False", allora funziona bene.

<ListBox Name="Tree" DockPanel.Dock="Top" 
    ItemsSource="{Binding DataSource.OrderedLibraryItems}" 
    Background="{StaticResource LibraryBackground}" 
    Width="220" HorizontalAlignment="Left" 
    BorderThickness="0" 
    VirtualizingStackPanel.IsVirtualizing="True" 
    VirtualizingStackPanel.VirtualizationMode="Standard" 
    ScrollViewer.IsDeferredScrollingEnabled="True" 
    ScrollViewer.CanContentScroll="False" 
    ItemTemplate="{StaticResource ListLibraryItemTemplate}" 
    SelectionMode="Single" 
    MouseDoubleClick="HandleMouseDoubleClick" 
/> 

Ma impostazione ScrollViewer.CanContentScroll = "false" disattiva la virtualizzazione e voglio usare la virtualizzazione per il mio ListBox quindi insieme fisso altezza e larghezza per TextBlock. Quindi, DockPanel non riorganizza i suoi figli se cambio il messaggio di stato.

Potrebbe essere un errore in ScrollViewer. Non dovrebbe cambiare la posizione di scorrimento se la dimensione non è cambiata.

+0

La risposta è una soluzione alternativa. Significa che non esiste una spiegazione comprovata e questa soluzione potrebbe non essere adatta a tutti. Potresti deselezionare il flag "la risposta risolve la domanda" in modo che le persone possano ancora pubblicare una soluzione reale? Ho lo stesso problema e le dimensioni di fissaggio non sono possibili per la mia app. – SandRock

+0

Se la "soluzione" funziona per il poster, è necessario controllarlo. Altri utenti potrebbero desiderare di fornire risposte migliori (ad esempio "soluzioni reali" in questo caso) e migliorare le soluzioni migliori, ma questo è il modo in cui SO funziona. Ho un caso in cui la mia risposta ha 5 upvotes anche se la risposta "corretta, controllata" ne aveva solo una :) Forse mi sbaglio su come funziona, questo è quello che penso. –

+0

Sì, hai ragione. Ho pubblicato una taglia invece :) – SandRock

-1

Poiché il layout viene ripristinato, è previsto che la casella di riepilogo selezioni il primo elemento (0).

Puoi provare a impostare la voce selezionata al numero di elementi esistenti nella casella di riepilogo:

Tree.SelectedIndex = Tree.Items.Count; 

non ho la prova questa soluzione sul tuo codice, ma l'ho usato in un altro progetto di miniera dove Ho avuto un problema simile.

Spero che aiuti.

+0

Grazie per la risposta. WPF non seleziona realmente il primo elemento quando si verifica il ridimensionamento: è solo il reset dello scrollviewer Offset verticale e orizzontale. Quando si verifica il problema, non voglio modificare la selezione dell'utente, ma impedire che lo scrollviewer resetta a zero. Mi dispiace ma la tua soluzione ha effetti collaterali indesiderati (modifica della selezione). – SandRock

+0

Errore mio, l'ho capito male. Per prevenire l'effetto collaterale, l'array degli elementi selezionati può essere memorizzato e reimpostato dopo l'aggiornamento. Comunque non è la soluzione al problema. – Pimenta

0

Prova questo:

listBox1.ScrollIntoView(listBox1.Items.GetItemAt(listBox1.Items.Count - 1)); 
+0

Il tuo codice fa scorrere ScrollViewer della ListBox fino all'ultima voce. Vorrei che lo scrollviewer rimanesse nella posizione precedente (che può essere da zero a N-1. – SandRock

0

In risposta a @ di user904627 risposta, qui è una versione migliorata del suo soluzione. Il problema con il semplice fixing Width e Height è il ListBox mantiene la stessa posizione anche se l'utente ridimensiona la finestra. Questo non è accettabile.

Per questo ho creato questo piccolo comportamento che fissa Larghezza e Altezza ma ascolti all'evento dell'elemento genitore SizeChanged di lasciare che il ListBox si ridimensiona quando la dimensione modifiche del contenitore.

il codice è qui: VirtualizedListBoxFixBehavior

Quando l'elemento padre viene ridimensionato, ho ripristinare Width e Height-double.NaN (in modo che il controllo può ridimensionare stesso) e in coda la porzione di codice che fissa le proprietà di dimensione di reale valori nel Dispatcher per l'esecuzione successiva.

Ma questo è ancora un soluzione di lavoro brutto ...

Problemi correlati