18
votes

First of all, a disclaimer, I'm working with .net 3.5's virtualizingstackpanel. If you get different behavior in future versions let me know. It's relatively simple to set up a test case with listviews that you can test this against.

I have an itemcontainer style in a virtualizingstackpanel that binds the property IsSelected to the viewmodel.

when I Select an unselected item in the view model that is off screen, and then scroll to that item, both the datacontext (viewmode) and the actual listviewitem have the IsSelected Property as true (expected behavior). The trigger is properly applied to the listviewitem highlighting it.

However, when i deselect the datacontext for an item that is not in view and then scroll down until the item is in view, upon reaching the item and creating it, the item's datacontext now has IsSelected = true and the listviewitem's IsSelected property is also true, so the listviewitem ends up with a selection rectangle from the trigger(incorrect behavior).

It's almost as if the ListViewItem's Properties are all restored on creation of the item ( this makes sense to me, but then they should bind the datacontext's values to the item afterward).

But that doesn't seem to be happening. Moreover, after failing to deselect the item and scrolling back to find it selected. If i then select/desect it, the binding has no effect on the item.

I can see no logical reason why it would work when selecting an item in the viewmodel that is offscreen and not when I Deselect an item offscreen. In both cases, the newly created item should need to be rebound to the current value of the viewmodel. However, one works and the other doesn't.

Any help or thoughts would be appreciated.

edit: Ah ok, so I just can't use recycling mode and binding it seems. Thanks devhedgehog. would give you the bounty but you need an answer. I swear I tried that earlier, but maybe I was not handling click events in the bound listview before so that i was breaking the binding on physical selection or something. I do remember attempting both modes at one point, but probably had something else interfering so it wouldn't work. Anyway it works now.

Since you mentioned it, I would like to preferably avoid keeping unnecessary code and inherit from virtualizingstackpanel instead of virtualizing panel. But I want to be able to set the horizontal scroll extent, which requires me to reimplement Iscrollinfo. However, I was having trouble getting virtualizingstackpanel to interact nicely with iscrollinfo

      <ListView
        x:Name="TestLV"
        VerticalAlignment="Stretch"
        HorizontalAlignment="Stretch"
        Background="Green" 
        ItemsSource="{Binding Path=AddedItems, Mode=OneWay}"
        SnapsToDevicePixels="True"
        VirtualizingStackPanel.VirtualizationMode="Recycling" 
        VirtualizingStackPanel.IsVirtualizing="true"
        ScrollViewer.IsDeferredScrollingEnabled="False"
        Grid.Column ="4"
        MouseDown="TestLV_MouseDown"
        >
      <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
          <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=OneWay}" />
          <Setter Property="Template">
            <Setter.Value>
              <ControlTemplate TargetType="{x:Type ListViewItem}">
                <Grid 
                    x:Name="SignalGrid"
                    Background="Transparent"
                    >
                  <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                  </Grid.ColumnDefinitions>
                  <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*"/>
                  </Grid.RowDefinitions>
                  <Border 
                      Name="Bd"
                      BorderBrush="{TemplateBinding BorderBrush}"
                      BorderThickness="{TemplateBinding BorderThickness}"
                      Padding="{TemplateBinding Padding}"
                      SnapsToDevicePixels="true">
                    <ContentPresenter 
                        x:Name="PART_Header"
                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                        />
                  </Border>
                  <ItemsPresenter
                      x:Name="ItemsHost"
                      Grid.Row="1"
                      Grid.Column="0"
                      />
                </Grid>
                <ControlTemplate.Triggers>
                  <Trigger Property="IsSelected"
                               Value="false">
                    <Setter 
                        TargetName="SignalGrid"
                        Property="Background"
                        Value="Transparent"
                        />
                  </Trigger>
                  <Trigger Property="IsSelected"
                             Value="true">

                    <Setter 
                        TargetName="SignalGrid"
                        Property="Background"
                        Value="Navy" 
                        />
                  </Trigger>
                </ControlTemplate.Triggers>
              </ControlTemplate>
            </Setter.Value>
          </Setter>
        </Style>
      </ListView.ItemContainerStyle>
      <ListView.ItemsPanel>
        <ItemsPanelTemplate>
          <VirtualizingStackPanel />
          <!--<Components:VirtualizingTilePanel 
              ChildSize="{Binding Path=GraphHeight}"
              />-->
        </ItemsPanelTemplate>
      </ListView.ItemsPanel>
      <ListView.Template>
        <ControlTemplate>
          <Grid >

            <ScrollViewer

                >
              <ItemsPresenter />
            </ScrollViewer>
          </Grid>
        </ControlTemplate>
      </ListView.Template>
      <!--Template Defining the layout of items in this treeview-->
      <ListView.Resources>
        <HierarchicalDataTemplate 
            ItemsSource ="{Binding Path = bits}"
            DataType="{x:Type ViewModels:BusViewModel}"
            >
          <Grid>
            <Components:CenteredTextBlock
                x:Name="CommentTextBlock"
                Foreground="Black"
                BorderThickness="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderThickness}"
                BorderBrush="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderBrush}"
                HorizontalAlignment="Stretch"
                Height="{Binding ElementName=graph_viewer, Path=GraphHeight, Mode=OneWay}"
                >
              <Components:CenteredTextBlock.MainText>
                <MultiBinding Converter="{StaticResource StringConcatConverter}">
                  <Binding Path="Alias" />
                  <Binding Path="SignalValueAtPrimaryMarker" />
                </MultiBinding>
              </Components:CenteredTextBlock.MainText>
            </Components:CenteredTextBlock>

          </Grid>
        </HierarchicalDataTemplate>
        <DataTemplate
            DataType="{x:Type ViewModels:BitViewModel}"
            >
          <Grid>
            <Components:CenteredTextBlock
                Foreground="Black"
                BorderThickness="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderThickness}"
                BorderBrush="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderBrush}"
                HorizontalAlignment="Stretch"
                Height="{Binding ElementName=graph_viewer, Path=GraphHeight, Mode=OneWay}">
              <Components:CenteredTextBlock.MainText>
                <MultiBinding Converter="{StaticResource StringConcatConverter}">
                  <Binding Path="Alias" />
                  <Binding Path="SignalValueAtPrimaryMarker" />
                </MultiBinding>
              </Components:CenteredTextBlock.MainText>
            </Components:CenteredTextBlock>
          </Grid>
        </DataTemplate>
        <DataTemplate
            DataType="{x:Type ViewModels:SelectableItemViewModel}"
            >
          <Grid>
            <Components:CenteredTextBlock
                Foreground="Red"
                BorderThickness="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderThickness}"
                BorderBrush="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderBrush}"
                HorizontalAlignment="Stretch"
                Height="{Binding ElementName=graph_viewer, Path=GraphHeight, Mode=OneWay}"
                MainText="{Binding Path = FullName}"

                />
          </Grid>
        </DataTemplate>
      </ListView.Resources>
    </ListView>
1
Why are you not using measureoverride of VirtualizingStackPanel? VirtualizingStackPanel would solve you this issue. Btw I checked your MeasureOverride method and you lack on features VirtualizingStackPanel has. You have no offset included. You are not keeping track of focused element. When you in recycling mode you will need to clean up before and after measuring children else the elements will be inserted in wront order... - dev hedgehog
So I used a tutorial on creating a virtualizing tile panel, this measureoverride is the one used there. Sorry I forgot to link it. I will edit my post. - James Joshua Street
Also, I take care of clean up in the cleanup items function. I don't know what offset you are referring to though. Can you post a link to the virtualizing stack panel measureoverride? Anyway, the only problem i have with the virtualizing panel at the moment is that you can't set IsSelected on an Item that doesn't exist. You have to change it in the viewmodel instead. However, I don't know how to apply the binding in the style to the correct viewmodel on recreation of the item. I Have tried calling applytemplate (FrameworkElement) on the ListViewItem after an item is newly created, but no luck - James Joshua Street
Please use standard VirtualizingStackPanel without your custom measure logic. There is nothing you added special in your logic that VirtualizingStackPanel cant do. And RecyclingMode should not be Recycle but instead leave it out or change to Standard. Its hard to understand you without a demo. Upload a demo please. You know, we love to see code examples. - dev hedgehog
The answer is no, VSP scrolls by units. That means it jumps over items. However it has the feature to scroll by pixels. If you using .NET 4.5 you will be able to activate that feature. But you are in 3.5. In .NET 3.5 only TreeView allows scrolling by pixels. Means if you want smooth scrolling just place your list inside TreeView. TreeView with a list inside is the same as ListBox. Sort of. Both do the same. - dev hedgehog

1 Answers

1
votes

It seems strange that this question actually appears to have been answered, yet is listed as not having an answer. So I will post dev hedgehog's comment/answer here.

Please use standard VirtualizingStackPanel without your custom measure logic. There is nothing you added special in your logic that VirtualizingStackPanel cant do. And RecyclingMode should not be Recycle but instead leave it out or change to Standard.