10

I have a strange behaviour with VirtualizingStackPanel. I have a list with items that contains TextBlock with TextWrap="Wrap". Here is the code:

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

It works pretty good, but if I try to scroll very fast (using mouse on emulator, not prommatically) there is some lagging in scroll, probably HorizontallOffset sometimes calculates wrong, and in the bottom in ends with very strange result (see image, right image demostrates normal behaviour).

scroll bug

After research I figured out that problem in combination VirtualizingStackPanel and TextBlock.TextWrap="Wrap", if I remove one element from this couple all works correctly.

But I need virtualization because of big items count, and TextWrap for correct text displaying.

So I think about making my own implementation of Virtualizing Panel, can you please guide me, how to do this, or how to fix current problem?

UPD: The problem:
on the first two images ListBox is already(!) scrolled to the bottom (it can't be scrolled down any more), but elements placed incorrectly, correct placing shown on the right image. This happens only if you will scroll very fast.

UPD2: Thanks to Milan Aggarwal. He provided a good case of my problem here. Seems that is really a bug in ListBox. Workaround, provided doesn't fit in my scenario, because I need to interact with controls inside ListBox item. Now I'm trying to catch ManipulationCompleted event and check if it is Inertial, if so that means scroll and I set focus to page:

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

P.S. thanks for good luck wishes;)

Alexander
  • 1,287
  • 1
  • 15
  • 34

4 Answers4

9

Inorder to overcome the black occurrence on scrolling you need to virtualize your scroll control. For that you should inherit IList and create a Collection of your own similar to ObservableCollection in which you will have to override the default indexer depending on your caching requirement and simultaneously maintain a cache for your items. I feel this might be what you are looking for: http://blogs.msdn.com/b/ptorr/archive/2010/08/16/virtualizing-data-in-windows-phone-7-silverlight-applications.aspx

There is a sample project on that page. Try that out.

I also feel that you are facing this problem http://blog.rsuter.com/?p=258. I guess this will be solved using virtualization itself. Hope it helps

Milan Aggarwal
  • 5,104
  • 3
  • 25
  • 54
1

What is the problem on 2nd screen? You mean large empty space after last message? Last message not being put at the bottom of page? Am I getting this right?

Actually i didn't try to reproduce your code and bug but my point is that since you are using 3rd party libs (Silverlight Toolkit) in your app, why not to use Coding4Fun Tools as well?

I wrote this dummy class:

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

Then i put this dummy code in main page constructor:

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;

And in xaml i put a listbox with chat bubble control of toolkit:

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

I ran it and saw no strange behaviours, maybe little bit of lagging. There is the result:

dat pic is huge

Hope i did some help.


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

Oleg
  • 422
  • 3
  • 13
  • no, this control also have this problem, just make items with different height and place inside them button for example, click on any button and scroll, this will reproduce the bug – Alexander Aug 17 '12 at 14:51
  • I've just remembered that i encountered this problem on my last project and i have no doubt it exists in my current one. But the conditions this bug appears on (fast scrolling) aren't normal for me so i didn't try to fix it. =) – Oleg Aug 17 '12 at 15:07
1

What is the problem at the second image? Is it that the BlackArea showed up at all, or is it that the ScrollView did not "bounceback" after the scrolling ended to fill the black area with content?

It is crucial to know this, because ... this effect is simply natural to WPF/WP7.. BTW. the scroller should "bounceback" after the over-scrolling, but it seems it has a bug and sometimes forgets to do it.

I'm asking because black/white areas are quite often not only on WP7, but and anywhere where there's a XAMLish ScrollViewer with inertial scrolling. You see, when scrolling faster than the platform can provide new items, you just scroll outside of the pre-calculated items and once you "over"scroll very far, the slowly-incoming items abruptly discover that there are no more items, hence - back empty area..

This happens mostly when some conditions happen at once: you scroll fast (yay), your bindings are slow (overly complicated expressions), your template-rendering is slow (complex non-reusable UI templates), the bindings does not know how many items there are (bound to IEnumerable not IList -> no .Count information!), or the renderer doesnt know how much height to reserve for an item (the item is not constant-height, but eveyr item has to be calculated to determine its own exact height).

If you want to correct it, you must fight with those causes first or you must implement your own Scrolling or hack into virtualizing stack panel and synchronize the feeding of items from bindingsource with the scrolling to let the scroller guess the total height better: for example, display only a small items at once, listen to scrolling events and add/remove next pack of items from head/tail of the list dynamically.. This will solve the problem by not allowing to scroll fast :)))))

I believe that constraining the item height is the easiest way. Can't you do something smart to make the items actually constant-sized?

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • no, items must be different height, they must display all their content. Milan Aggarwal posted a great link with demonstration of problem – Alexander Aug 17 '12 at 14:55
  • I understand that and I have quite a few apps with the same problem. Really, you have to focus on the few things I've pointed out. Some combinations of then simply force such effect. Another thing, you may try to disable "inertia-scrolling" and leave the normal, flat-scrolling, but that usually is also unfavorable as this lowers the "feeling".. I cannot help you further without seeing your exact case. I can only point to the common causes. The problem it self is all about performance of your code, bindings, and rendering. – quetzalcoatl Aug 17 '12 at 19:33
1

I suggest you create the simplest possible function that computes a height for the wrapped textblock and bind to that. This way, you get the benefit of variable height items in the listbox, but get to keep using the virtualizing stack panel.

Chui Tey
  • 5,436
  • 2
  • 35
  • 44