将项目移动到列表顶部时,WPF AlternationIndex会回绕

时间:2015-10-21 20:36:47

标签: c# wpf listview data-binding

我尝试使用行号创建一个GridView,并且为用户提供上/下按钮以更改行的顺序。许多帖子建议使用AlternationIndex(例如here)并且几乎完美地工作,并且当用户按下向上/向下按钮以更改行的顺序时它会处理。但是,当您将项目移动到第一个位置时,AlternationIndex会失败 - 此时,它应该显示0,但它会转换为AlternationCount中的最后一个值。

示例:

    <ListView AlternationCount="1000" Name="_stuff" Grid.Row="0">
                <ListView.View>
                    <GridView>
                        <GridViewColumn
                               Header="Alit" Width="30"
                               DisplayMemberBinding="{Binding (ItemsControl.AlternationIndex),
                               RelativeSource={RelativeSource AncestorType=ListViewItem}}" />

                        <GridViewColumn Header="ColumnName" DisplayMemberBinding="{Binding}" Width="240"/>
                    </GridView>
                </ListView.View>
            </ListView>

然后我的代码隐藏是:

 ObservableCollection<string> data = new ObservableCollection<string>() { "First", "Second", "Third", "Forth", "Last" };

 public MainWindow()
 {
      InitializeComponent();
      _stuff.ItemsSource = data;
  }

  private void UpDownButton_Click(object sender, RoutedEventArgs e)
  {
      //User wants to change the order -- remove the item from the observable 
      //collection and reinsert it at the new position.  
      data.Remove("Last"); //First remove, then re-insert.

      //If you move the last item to *middle* of list, it works fine and the index
      //is correct. (All other items are pushed down by 1, like you'd expect.)
      //But move to the *top* of the list and the new index is 999?

      //THIS WOULD WORK FINE AND ALL INDEXES ARE CORRECT
      //    data.Insert(3, "Last"); //Insert to middle of list

      //But this gives an index of 999???? Seems to be wrapping
      //around to the last AlternationCount. But why? 
      data.Insert(0, "Last"); //insert to top of list
  }

任何想法如何让顶部的新项目的alternationIndex为0而不是999?

1 个答案:

答案 0 :(得分:1)

我认为这是一个错误。它表现得像那样是不可接受的。不知何故,AltenrationIndex设置错误,您可以找到关于此的源代码,但坦率地说,它并不是很容易理解。这背后有一些算法涉及虚拟化机制以及它们实现的模式。以下是我认为可能涉及更新AlternationIndex方法SetAlternationIndex的源代码。它在一些回调中被调用,例如当AlternationCount被更改时,项目被删除,......

此处涉及的一些难以理解的概念包括ItemBlockoffset以及GeneratorDirection。这就是为什么我很难理解那里的代码。

我发布这个答案是因为我找到了一个简单的解决方案,虽然我不确定它是否存在性能问题,但我确信它应该可以接受。

如果重新生成项容器,则AlternationIndex应该再次排序。因为在虚拟化模式下,项容器的数量只等于可见元素的数量,所以我认为这不会导致性能问题。 ItemContainerGenerator实现了具有名为IItemContainerGenerator的方法的接口RemoveAll。通过使用此方法,将删除所有已实现(生成)的项容器,并且将再次自动生成它们,从而导致AlternationIndex按正确顺序设置。

另外,正如您所说,只有在第一个索引处插入项目时才会出现问题。所以我们只需要在特定情况下使用这个技巧:

private void UpDownButton_Click(object sender, RoutedEventArgs e)
{
  //before changing any item at index 0 such as by inserting 
  //some new one or even use the method Move(... , 0),      
  //We need to clear all the realized item containers
  var cg = _stuff.ItemContainerGenerator as IItemContainerGenerator;
  cg.RemoveAll();
  //now just proceed the code
  //we can in fact use this instead data.Move(data.Count - 1, 0);
  data.Remove("Last");
  data.Insert(0, "Last");      
}

这是使用ICollectionView.Refresh()方法的另一个非常好的解决方案。您只需要刷新它(但当然只应在出现问题时应用):

private void UpDownButton_Click(object sender, RoutedEventArgs e)
{      
  //we can in fact use this instead data.Move(data.Count - 1, 0);
  data.Remove("Last");
  data.Insert(0, "Last");      
  CollectionViewSource.GetDefaultView(data).Refresh();
}