2012-07-02 77 views
7

我需要知道ListBox第一次完成渲染的時間,以便我可以將其滾動到頂部以向用戶顯示列表上的第一項。如何知道ListBox在Silverlight中完成呈現的時間?

我有一個使用RichTextBox在它的DataTemplate一個ListBox

<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> 

ListBox綁定到ObservableCollection

我有一個問題與ListBox的滾動 - 如果RichTextBox的高度明顯高於ListBox用戶將無法滾動到RichTextBox底部的更大。 ListBox將跳轉到列表中的下一個項目。滾動條滑塊的高度也會改變。這是因爲RichTextBox的實際高度僅在實際渲染時計算。當它離開屏幕時,高度恢復爲較小的值(我認爲代碼假定文本可以全部適合單行而不是必須被包裝)。

我跟蹤了這​​些問題,直到ListBox使用VirtualisingStackPanel來繪製項目。當我用簡單的StackPanel取代那些問題時,這些問題就消失了。

然後,這就產生了我現在的問題,即ListBox在初始加載時滾動到列表的底部。 ListBox上的LoadedLayoutUpdated事件發生在數據加載之前。我試着聽出了對視圖模型PropertyChanged事件時ObservableCollection被初始化:

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

這觸發太早爲好。在此事件被觸發並導致ListBox滾動到底部之後,列表呈現

回答

0

最後,我不得不用雜牌組裝電腦:

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(); 
} 

如果有人知道一個更好的方法請立即發表您的答案。

0

嘗試滾動加載的處理程序,但延遲一點Dispatcher。類似的東西

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

它可以幫助。

+0

嗯。我對這樣的事情保持警惕 - 特別是因爲列表中可能有很多項目 - 但我會放棄它。 – ChrisF

+0

由於渲染髮生在UI線程中,並且Dispatcher.BeginInvoke將滾動操作推送到UI線程的隊列 - 實際上,無論您的列表有多大都無關緊要。滾動應在其他當前的UI線程指令完成後執行。當然,如果你的集合(或控制/模板的其他部分)被創建爲異步 - 它不會工作。但它是另一種情況。通常情況下,它適用於簡單的場景。 – Kreol

+0

好點 - 雖然列表異步填充.... – 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 
    } 

然後在XAML的一些命令,你應該能夠控制和設置您的視圖模型的SelectedItemFromModel財產使用作爲

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

相關問題