Problem

Refresh issue with DeferrableObservableCollection

Posted 6 years ago by Avatar David Mullin
I have a listbox sitting on top of a DeferrableObservableCollection, like this:
<ListBox Grid.Row="1" ItemsSource="{Binding Path=DeferrableList}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>

                <Label Grid.Row="0" Grid.Column="0">Label</Label>
                <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Text, UpdateSourceTrigger=PropertyChanged}" />
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
I have things wired up so that, when the user types in the TextBox of a ListBoxItem, an event is raised which results in adding an empty item to the DeferrableObservableCollection. If I just add it to the list, everything works. If I called BeginUpdate/EndUpdate, however, the currently active TextBox control loses focus and the focus goes nowhere.

I have a reproduction of this scenario, if that will help you.

Comments (7)

Posted 6 years ago by Actipro Software Support - Cleveland, OH, USA
Hi David,

When you do Begin/EndUpdate with that collection, it's not tracking or caching up the individual collection changed events right now. Instead of just fires a NotifyCollectionChangedAction.Reset if any change was made.

My guess based on your post is that the Reset is being interpreted by ListBox to recreate all the item templates. Maybe you could track the selected item before and after your update and restore focus to it after.

Actipro Software Support
Posted 6 years ago by David Mullin
I tried going down that route. The issue is that, even if I am able to retain which ListBoxItem is active, since I'm using a DataTemplate for the ListItemTemplate, it is difficult to identify the actual control inside of the current ListBoxItem. Furthermore, since this is happening in response to the user typing in the field, it would also be extremely difficult to put the cursor back in the right place within the control.

Would it be possible to change Begin/EndUpdate so that they notify differently?

David
Posted 6 years ago by Actipro Software Support - Cleveland, OH, USA
Unfortunately there are only 5 types of notification that are allowed per the enum and none of the other ones would make sense.

Even if we did cache the original events, say you made 100 changes, we'd have to fire them all, meaning it would cause numerous unnecessary updates to the UI controls that were attached. Also it may cause a number of issues if the cached events reference items that are no longer in the collection due to intermediate edits.

Could you describe your specific usage for the Begin/End and what you do to modify the collection inside those? That might help us think of something else.

Actipro Software Support
Posted 6 years ago by David Mullin
Hmmm. Yes, I see the issue. Looking at ObservableCollection with reflector, I see that the CollectionChanged event includes information for the item added/moved/removed - I gather that the ListBox intelligently inspects this information to figure out what to do? Do you know? Looking at Selector.OnItemsChanged, I honestly can't quite tell...most of the code in there looks like it is dealing with maintaining the current selection.

The specific usage scenario is this:
I've got a ListBox that displays a list of items. Some of the items should be hidden (because of business logic). The items have a DataTemplate that presents one or more UI elements for the item. When the user makes changes to the contents of the UI elements, this sometimes triggers changes in the business logic which results in other items in the list being hidden or shown. One possible condition is that a field goes from empty to non-empty (i.e., the user starts typing in a field)

For the first itteration of this, we just set the Visibility on the hidden items. For a variety of reasons (performance, issues with grouping, etc.), we changed it so that there were two lists - one with all of the items, and an ObservableCollection with only the visible items, and the ListBox sat on this later collection. The issue with this was that, if there was a large batch of changes, the repeated updates were painfully slow. So, we changed it to an DeferrableObservableCollection, which solved the performance issue, but introduced the lost-focus issue.
Posted 6 years ago by Actipro Software Support - Cleveland, OH, USA
Hi David,

We came up with something here I'd like you to try. As long as you only do Add and Remove operations, we changed our code to cache them up and fire a single Add and/or single Remove operation (each containing the list of added/removed items since BeginUpdate) when EndUpdate is called instead of blindly doing a Reset event.

Please email us and we'll set you up with a preview build to try and see if it helps.

Actipro Software Support
Posted 6 years ago by Actipro Software Support - Cleveland, OH, USA
David,

Thanks for the sample, we did some testing and found that your list doesn't update if we cache things up as described. The only way we found to get it to refresh was to either keep it like we originally had things (but with your focus problem), or fire each event individually (like each Add event must have only a single related item, not multiple) when EndUpdate is called.

The latter defeats the purpose of the collection since you end up firing the same things as if there were no Begin/EndUpdate wrapper.

So unfortunately we'll have to go back to our original code.

Actipro Software Support
Posted 6 years ago by David Mullin
Yeah, I was afraid of that. I had tried something similair, and got equally unhelpful results. Oh, well.

But, thanks for trying!

David

Add a Comment

Please log in to a validated account to post comments.