替换ItemsSource中的项目,使DataGrid排序异常

时间:2019-01-30 10:25:22

标签: .net wpf xaml

我有一个通过单击标题按用户排序的DataGrid。 选择一个项目后,我必须从数据源刷新它。

当我替换ItemsSource中的新项目时,该行正在移动。 排序必须在一列上,该列的所有项目都必须具有相同的值。

MainWindow.xaml

<Window x:Class="WpfApp4.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"      
    xmlns:vm="clr-namespace:WpfApp4"   
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800"
    DataContext="{DynamicResource ResourceKey=viewModel}">

<Window.Resources>
    <vm:MainWindowViewModel x:Key="viewModel" />
</Window.Resources>

<Grid>
    <DataGrid AutoGenerateColumns="True" CanUserSortColumns="True" 
              ItemsSource="{Binding Persons}" SelectedItem="{Binding SelectedPerson}"/>

</Grid>

MainWindowViewModel.cs

using System.Collections.ObjectModel;
using System.ComponentModel;

namespace WpfApp4
{
public class MainWindowViewModel : INotifyPropertyChanged
{
    public MainWindowViewModel()
    {
        Persons = new ObservableCollection<Person>()
        {
            new Person()
            {
                Name = "Foo", Age = 10
            },
             new Person()
            {
                Name = "Bar", Age = 10
            },
            new Person()
            {
                Name = "Yolo", Age = 10
            },
        };
    }

    private ObservableCollection<Person> _persons;
    public ObservableCollection<Person> Persons
    {
        get => _persons;
        set
        {
            _persons = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Persons"));
        }
    }        

    private Person _selectedPerson;
    public Person SelectedPerson
    {
        get => _selectedPerson;
        set
        {
            _selectedPerson = value;
            SelectedPersonChanged();
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedPerson"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void SelectedPersonChanged()
    {
        if (SelectedPerson != null)
        {
            //Get a refreshed instance of person from DataSource
            //For the purpose of the example, we admit that the values are the same
            Person updatedPerson = new Person() { Age = SelectedPerson.Age, Name = SelectedPerson.Name };

            //Update in collection
            int previousIndex = Persons.IndexOf(SelectedPerson);
            Persons[previousIndex] = updatedPerson;
            _selectedPerson = updatedPerson;               
        }
    }
}

public class Person  
{
    public string Name { get; set; }
    public int Age { get; set; }
}
}

实时示例:

Preview

任何想法如何在不破坏行顺序的情况下替换项目吗?

谢谢

修改

我最终使用AutoMapper将Person从数据源复制到SelectedPerson。

在ViewModel构造函数中:

Mapper.Initialize(cfg => cfg.CreateMap<Person, Person>());            

private void SelectedPersonChanged()
    {
        if (SelectedPerson != null)
        {
            //Get a refreshed instance of person from DataSource
            //For the purpose of the example, we admit that the values are the same
            Person updatedPerson = new Person()
            {
                Age = SelectedPerson.Age, Name = SelectedPerson.Name
            };

            Mapper.Map(updatedPerson, SelectedPerson);               
        }
    }

2 个答案:

答案 0 :(得分:0)

我认为更好的方法是编辑现有实例,而不是删除它并添加新实例。

只需使Person实现INotifyPropertyChanged并编辑属性即可。

  private void SelectedPersonChanged()
    {
        if (SelectedPerson != null)
        {
            var personFromDB = GetFromDB(SelectedPerson.Id);

             SelectedPerson.Age = personFromDB.Age;
             SelectedPerson.Name = personFromDB.Name;
        }
    }

答案 1 :(得分:0)

您可以尝试按多个字段进行排序。如果您的数据具有一个id字段或其他唯一不变的字段,则可能效果最好。否则,如果从数据库中重新加载名称时,该名称仍可能会跳动。

在这里,您可以使用列出的两个字段(年龄/姓名)来做到这一点。当您按年龄排序时,将添加按名称的辅助排序。并且,当您按名称排序时,会添加按年龄排序的辅助排序。

xaml:

<DataGrid AutoGenerateColumns="True" CanUserSortColumns="True"
    Sorting="DataGrid_Sorting" 
    ItemsSource="{Binding Persons}" SelectedItem="{Binding SelectedPerson}"/>

代码:

    private void DataGrid_Sorting(object sender, DataGridSortingEventArgs e)
    {
        var v = CollectionViewSource.GetDefaultView((sender as DataGrid).ItemsSource);
        v.SortDescriptions.Clear();

        // Set column sort direction - otherwise you won't see the arrow on the column header
        if (!e.Column.SortDirection.HasValue)
            e.Column.SortDirection = ListSortDirection.Descending;
        e.Column.SortDirection = e.Column.SortDirection.Value == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;

        // Add sort description
        v.SortDescriptions.Add(new SortDescription(e.Column.SortMemberPath, e.Column.SortDirection.Value));

        // Add secondary sort description (age or name)
        var secondarySort = e.Column.SortMemberPath == "Age" ? "Name" : "Age";
        v.SortDescriptions.Add(new SortDescription(secondarySort, ListSortDirection.Ascending));

        // Add more sort descriptions, as needed.

        // We handled it...
        e.Handled = true;
    }