Ошибка виртуализацииStackPanel и TextWrapping? Windows Phone

У меня странное поведение с VirtualizingStackPanel. У меня есть список с элементами, которые содержат TextBlock с TextWrap="Wrap". Вот код:

<ListBox x:Name="messagesList" ItemsSource="{Binding Messages}" >
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>
    </ListBox.ItemContainerStyle>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <toolkit:ContextMenuService.ContextMenu>
                    <toolkit:ContextMenu>
                    ...
                    </toolkit:ContextMenu>
                </toolkit:ContextMenuService.ContextMenu>
                <CheckBox Style="{Binding Own, Converter={StaticResource MsgTypeToStyle}}"
                          Tag="{Binding TimeString}"
                          IsEnabled="True">
                    <TextBlock Text="{Binding Content}" TextWrapping="Wrap"/>
                </CheckBox>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Он работает очень хорошо, но если я попытаюсь прокрутить очень быстро (используя мышь на эмуляторе, а не промативно), есть некоторое отставание в прокрутке, возможно, HorizontallOffset иногда вычисляет неправильно, а внизу в конце с очень странным результатом (см. изображение, правое изображение демонстрирует нормальное поведение).

scroll bug

После исследования я выяснил, что проблема в комбинации VirtualizingStackPanel и TextBlock.TextWrap="Wrap", если я удалю один элемент из этой пары, все работает правильно.

Но мне нужна виртуализация из-за большого количества элементов и TextWrap для правильного отображения текста.

Итак, я думаю о том, чтобы сделать мою собственную реализацию Virtualization Panel, можете ли вы, пожалуйста, направить меня, как это сделать или как исправить текущую проблему?

UPD: проблема:
на первых двух изображениях ListBox уже (!) прокручивается снизу (его больше нельзя прокручивать), но элементы помещены неправильно, правильное размещение показано на нужном изображении. Это происходит, только если вы прокрутите очень быстро.

UPD2: благодаря Милану Аггарвалю. Он предоставил хороший пример моей проблемы здесь. Кажется, это действительно ошибка в ListBox. Обходной путь, который не соответствует моему сценарию, потому что мне нужно взаимодействовать с элементами управления внутри элемента ListBox. Теперь я пытаюсь поймать событие ManipulationCompleted и проверить, есть ли он Inertial, если это означает, что прокрутка и я устанавливаю фокус на страницу:

    void messagesList_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
    {
        if (e.IsInertial)
            this.Focus();
    }

P.S. спасибо за пожелания удачи;)

Ответ 1

Чтобы преодолеть черное появление при прокрутке, вам необходимо виртуализировать свой элемент управления прокруткой. Для этого вы должны наследовать IList и создать Collection своего собственного, похожего на ObservableCollection, в котором вам придется переопределить индексатор по умолчанию в зависимости от вашего требования кэширования и одновременно поддерживать кэш для ваших элементов. Я чувствую, что это может быть то, что вы ищете: http://blogs.msdn.com/b/ptorr/archive/2010/08/16/virtualizing-data-in-windows-phone-7-silverlight-applications.aspx

На этой странице есть образец проекта. Попробуйте это.

Я также чувствую, что вы столкнулись с этой проблемой http://blog.rsuter.com/?p=258. Я думаю, это будет решено с помощью самой виртуализации. Надеюсь, что это поможет.

Ответ 2

В чем проблема на втором экране? Вы имеете в виду большое пустое место после последнего сообщения? Последнее сообщение не помещается внизу страницы? Правильно ли я это понимаю?

На самом деле я не пытался воспроизвести код и ошибку, но я хочу сказать, что, поскольку вы используете сторонние библиотеки (Silverlight Toolkit) в своем приложении, почему бы не использовать Coding4Fun Tools?

Я написал этот фиктивный класс:

public class Message
{
    public string Time { get; private set; }
    public string Content { get; private set; }

    public Message(string time, string content)
    {
        Time = time;
        Content = content;
    }
}

Затем я помещаю этот фиктивный код в конструктор главной страницы:

var _messages = new ObservableCollection<Message>();

for (int i = 0; i < 50; i++)
{
    _messages.Add(new Message("12:40", "Teh very long string which should be wrapped. Pavel Durov gives WP7 Contest winners less money than he did for Android/iOS winners. FFFUUUUUUUUUUUUUU "));
}

this.ListBox.ItemsSource = _messages;

И в xaml я помещаю список с чатом для управления набором инструментов:

<ListBox x:Name="ListBox">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <c4fToolkit:ChatBubble>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <TextBlock Grid.Row="0"
                               Text="{Binding Content}"
                               TextWrapping="Wrap"/>
                    <TextBlock Grid.Row="1"
                               Text="{Binding Time}"
                               HorizontalAlignment="Right"/>
                </Grid>
            </c4fToolkit:ChatBubble>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Я запустил его и не увидел странного поведения, возможно, немного отстающего. Результат:

dat pic is huge

Надеюсь, я немного помог.


Удачи с конкурсом, я б сам поучавший, но не верю, что за 1,5 месяца удастся сделать приличный клиент.

Ответ 3

В чем проблема второго изображения? Разве что BlackArea вообще не появлялся, или разве ScrollView не "откатился" после завершения прокрутки, чтобы заполнить черную область контентом?

Очень важно знать это, потому что... этот эффект просто естественен для WPF/WP7.. КСТАТИ. скроллер должен "отскакивать" после чрезмерной прокрутки, но, похоже, он имеет ошибку и иногда забывает сделать это.

Я спрашиваю, потому что черные/белые области довольно часто не только на WP7, но и где угодно, где есть XAMLish ScrollViewer с инерционной прокруткой. Видите ли, когда прокрутка быстрее, чем платформа может предоставлять новые элементы, вы просто просматриваете за пределами предварительно рассчитанных элементов, и как только вы "переходите" очень далеко, медленно входящие предметы внезапно обнаруживают, что больше нет предметов, следовательно, назад пустая область.

Это происходит в большинстве случаев, когда происходят некоторые условия: вы быстро прокручиваете (yay), ваши привязки медленные (чрезмерно сложные выражения), ваш шаблон-рендеринг медленный (сложные непеременные шаблоны пользовательского интерфейса), привязки не знают сколько элементов там (привязано к IEnumerable not IList → no.Count information!), или рендерер не знает, сколько высоты зарезервировать для элемента (элемент не является постоянным, но элемент eveyr имеет для вычисления его собственной точной высоты).

Если вы хотите его исправить, сначала необходимо бороться с этими причинами, или вы должны реализовать свою собственную прокрутку или взломать панель виртуализации стека и синхронизировать подачу элементов из источника привязки с помощью прокрутки, чтобы позволить скроллеру угадать общую высоту лучше: например, отображать только мелкие предметы одновременно, прослушивать прокрутку событий и динамически добавлять/удалять следующий пакет элементов из головы/хвоста списка.. Это решит проблему, не позволяя быстро прокручивать:)))) )

Я считаю, что ограничение высоты элемента является самым простым способом. Разве вы не можете сделать что-то умное, чтобы сделать предметы на самом деле постоянными?

Ответ 4

Я предлагаю вам создать простейшую возможную функцию, которая вычисляет высоту для завернутого текстового блока и связывается с ним. Таким образом, вы получаете преимущество элементов переменной высоты в списке, но можете продолжать использовать панель виртуализации стека.