如何知道ListBox何时在Silverlight中完成渲染?

时间:2012-07-02 10:32:00

标签: silverlight listbox rendering

我需要知道ListBox第一次完成渲染的时间,以便我可以将其滚动到顶部,向用户显示列表中的第一个项目。

我有ListBox RichTextBox使用DataTemplate

<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的底部{1}}。 ListBox将跳转到列表中的下一个项目。滚动条滑块的高度也会改变。这是因为RichTextBox的实际高度仅在实际渲染时计算。当它离开屏幕时,高度会恢复到较小的值(我认为代码假定文本可以全部放在一行而不必包裹)。

我将这些问题跟踪到ListBox使用VirtualisingStackPanel来绘制项目。当我用一个简单的StackPanel替换它时,这些问题就消失了。

然后创建了我现在遇到的问题,即ListBox在初始加载时滚动到列表的底部。 Loaded上的LayoutUpdatedListBox事件发生在加载数据之前。初始化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滚动到底部。

3 个答案:

答案 0 :(得分:0)

尝试滚动Loaded处理程序,但Dispatcher稍微延迟一下。像这样的东西

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

这可能有所帮助。

答案 1 :(得分:0)

最后我不得不使用kludge:

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

如果有人知道更好的方法,请立即发布您的答案。

答案 2 :(得分: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中使用

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

在某些命令中,您应该能够控制和设置视图模型的SelectedItemFromModel属性。

相关问题