18

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.

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>
ouflak
  • 2,458
  • 10
  • 44
  • 49
James Joshua Street
  • 3,259
  • 10
  • 42
  • 80
  • 5
    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 Jan 30 '14 at 09:07
  • 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 Jan 30 '14 at 17:27
  • 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 Jan 30 '14 at 17:35
  • Again if you user VirtualizingStackPanel without overriding any measure method you will do fine. VirtualizingStackPanel knows exactly how to virtuale items, measure them, and release DataContext properly when item no more needed. – dev hedgehog Jan 31 '14 at 07:23
  • so the reason i thought i had to override measure is because I want the children to choose a size = the available horizontal width of the Panel. – James Joshua Street Jan 31 '14 at 21:07
  • so the reason i thought i had to override measure is because I want the children to choose a size = the available horizontal width of the Panel. is there a way i can do that while inheriting from virtualizingstackpanel – James Joshua Street Jan 31 '14 at 22:11
  • VirtualizingStackPanel is passing to its children what is been given to VirtualizingStackPanel. Means the owner control. ListBox, TreeView, Grid, DockingPanel... Show us your layout please. – dev hedgehog Feb 01 '14 at 20:07
  • I have the same problem and I cannot use VirtualizingStackPanel because I need smooth scrolling which it can't do. My code is based on http://www.marauderzstuff.com/PermaLink,guid,b31f2278-9103-4c84-8520-8adf2aef7fbc.aspx and others. So what options do we have without VirtualizingStackPanel please? – ygoe Feb 02 '14 at 11:08
  • I added the layout, it's possible that in my situation if I can override the item's measure that I can pass the desired value and not need a custom panel implementation. However, i'm surprised that how to rebind the items in a panel is not more commonly known. It seems that not many people are creating custom virtualizing panels – James Joshua Street Feb 04 '14 at 19:41
  • so i figured out how to get my stuff to mostly work with virtualizingstackpanel. However, the weird thing is that it is still having the same bug. Also it doesn't make sense because if i change the bound data by selecting an item that is offscreen it works, but when i deselect it it is not working. I think i have some bug elsewhere in my code, so I doubt I will need an answer for this. Also it looks like most likely there is nothing special that has to be done to reset the bindings. it is all done by the generator behind the scenes. I read through the microsoft code and it doesn't seem special – James Joshua Street Feb 06 '14 at 03:41
  • 1
    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 Feb 06 '14 at 21:01
  • anyway your answer fixed my main problem. I am curious though, virtualizing stack panel scrolls using items in its iscrollinfo implementation. Is it possible to store horizontalextent and viewportwidth in pixels? I think the reason why I didn't use virtualizing stack panel is that I was worried that the VSP implementation would at some point assume that an iscrollinfo method i overrode would return items instead of pixels. It's pretty weird for it to be measuring in one direction in items for virtualization purposes but in pixels in the other – James Joshua Street Feb 06 '14 at 22:23
  • 6
    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 Feb 07 '14 at 18:24

1 Answers1

1

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.

Community
  • 1
  • 1
ouflak
  • 2,458
  • 10
  • 44
  • 49