2012-07-02 12 views
7

Ich muss wissen, wenn eine ListBox das Rendern zum ersten Mal beendet hat, so dass ich es nach oben scrollen kann, um den Benutzer mit dem ersten Element in der Liste zu präsentieren.Woher weiß ich, wann eine ListBox in Silverlight gerendert wurde?

Ich habe ein ListBox die RichTextBox verwendet darin DataTemplate ist:

<DataTemplate x:Key="HelpTextTemplate"> 
    <Grid> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*"/> 
      <ColumnDefinition Width="Auto"/> 
     </Grid.ColumnDefinitions> 
     ... 
     <ContentControl> 
      ... 
      <RichTextBox x:Name="HelpTextContent" Grid.Row="1" 
         Tag="{Binding Path=HelpObject.Text, Mode=TwoWay}" 
         TextWrapping="Wrap" 
         HorizontalAlignment="Stretch" 
         Margin="0,0,20,0" 
         Loaded="RichTextBox_Loaded" 
         ContentChanged="RichTextBox_ContentChanged" 
         SelectionChanged="RichTextBox_SelectionChanged"/> 
      ... 
     </ContentControl> 
     ... 
    </Grid> 
</DataTemplate> 

Die ListBox an einen ObservableCollection gebunden ist.

hatte ich ein Problem mit dem Scrollen der ListBox - wenn Höhe der RichTextBox größer war als der die ListBox der Benutzer nicht auf den Boden der RichTextBox bewegen kann. Die ListBox würde zum nächsten Element in der Liste springen. Die Höhe des Schiebereglers der Bildlaufleiste würde sich ebenfalls ändern. Dies liegt daran, dass die tatsächliche Höhe der RichTextBox nur berechnet wird, wenn sie tatsächlich gerendert wird. Wenn es nicht auf dem Bildschirm angezeigt wird, wird die Höhe auf einen kleineren Wert zurückgesetzt (ich denke, der Code geht davon aus, dass der Text in eine einzige Zeile passt und nicht umbrochen werden muss).

Ich verfolgt diese Probleme bis zur ListBox Verwendung eines VirtualisingStackPanel, um die Elemente zu zeichnen. Als ich das mit einem einfachen StackPanel ersetzte, gingen diese Probleme weg.

Dies erstellt dann das Problem, das ich jetzt habe, das ist, dass die ListBox scrollt an den unteren Rand der Liste bei der ersten Belastung. Die Ereignisse Loaded und LayoutUpdated auf der ListBox auftreten, bevor die Daten geladen wurden. Ich habe versucht, für das PropertyChanged Ereignis auf dem View-Modell zu hören, wenn die ObservableCollection initialisiert:

void editViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) 
{ 
    switch (e.PropertyName) 
    { 
     case "ListDataSource": 
      // Try to scroll to the top of the ListBox 
      break; 
    } 
} 

dies auch zu früh ausgelöst wird. Die Liste wird gerendert nach Dieses Ereignis wird ausgelöst und bewirkt, dass die ListBox nach unten scrollt.

Antwort

0

Am Ende hatte ich eine Flickschusterei zu verwenden:

private System.Threading.Timer timer; 
void editViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) 
{ 
    switch (e.PropertyName) 
    { 
     case "ListDataSource": 
      TimerCallback callback = TimerResult; 
      timer = new Timer(callback, null, 750, 0); 
      break; 
    } 
} 

private void TimerResult(Object stateInfo) 
{ 
    Dispatcher.BeginInvoke(() => 
    { 
     if (this.ItemsList.Items.Count > 0) 
     { 
      this.ItemsList.UpdateLayout(); 
      this.ItemsList.SelectedIndex = 0; 
      this.ItemsList.ScrollIntoView(this.ItemsList.Items[0]); 
     } 
    }); 

    timer.Dispose(); 
} 

Wenn jemand nun einen besseren Weg, bitte posten Sie Ihre Antwort weiß.

0

Versuchen Sie, auf Loaded-Handler zu scrollen, aber verzögern Sie es ein wenig vom Dispatcher. So etwas wie das

void OnLoaded(...) 
{ 
    Dispatcher.BeginInvoke(() => {/*Scroll your ListBox here*/}); 
} 

Es könnte helfen.

+0

Hmm. Ich bin vorsichtig bei solchen Dingen - vor allem, weil es eine Menge Dinge in der Liste geben könnte - aber ich werde es versuchen. – ChrisF

+0

Da das Rendering im UI-Thread stattfindet und Dispatcher.BeginInvoke den Scrollvorgang an die Warteschlange des UI-Threads weiterleitet, spielt es eigentlich keine Rolle, wie groß Ihre Liste ist. Scrollen sollte durchgeführt werden, nachdem andere aktuelle UI-Thread-Anweisungen ausgeführt wurden. Natürlich, wenn Ihre Sammlung (oder andere Teile der Kontrolle/Vorlage) async erstellt wird - es funktioniert nicht.Aber es ist ein anderer Fall .. Normalerweise für einfache Szenarien funktioniert es. – Kreol

+0

Guter Punkt dort - obwohl die Liste asynchron bevölkert ist .... – ChrisF

0
public class AutoScrollBehavior : Behavior<ListBox> 
    { 
     #region Properties 

     public object ItemToScroll 
     { 
      get { return GetValue(ItemToScrollProperty); } 
      set { SetValue(ItemToScrollProperty, value); } 
     } 

     public static readonly DependencyProperty ItemToScrollProperty = DependencyProperty.Register("ItemToScroll", typeof(object), typeof(AutoScrollBehavior), new PropertyMetadata(ItemToScrollPropertyChanged)); 

     private static void ItemToScrollPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      ListBox lb = ((AutoScrollBehavior)d).AssociatedObject; 
      lb.UpdateLayout(); 
      ((AutoScrollBehavior)d).AssociatedObject.ScrollIntoView(e.NewValue); 
     } 
     #endregion 
    } 

dann in der XAML verwenden als

<ListBox SelectedItem="{Binding SelectedItemFromModel, Mode=TwoWay}"> 
<i:Interaction.Behaviors> 
                   <Behaviors:AutoScrollBehavior ItemToScroll="{Binding SelectedItemFromModel}"/> 
                  </i:Interaction.Behaviors> 
</ListBox> 

auf einige Befehl sollten Sie in der Lage sein SelectedItemFromModel Eigenschaft Ihrer Ansicht Modell zu steuern und einstellen.

Verwandte Themen