Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding to data breaking on VirtualizingStackPanel

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>
like image 500
James Joshua Street Avatar asked Jan 30 '14 01:01

James Joshua Street


1 Answers

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.

like image 194
ouflak Avatar answered Oct 18 '22 09:10

ouflak