C# WPF Datagrid не выполняет динамическую сортировку при обновлении данных

У меня проблемы с моей сеткой данных:

Когда я обновляю некоторые данные (из модели), они отображаются в моей DataGrid, но если щелкнуть заголовок, чтобы отсортировать столбец, он начинает двигаться в сторону, когда я обновляю существующие данные.

Вот 2 примера 1:

  • Если я добавляю новое значение, оно не появляется в конце (как это происходит, когда я не сортирую сетку данных), но отображается в неправильном месте (каждый раз в одном и том же месте).
  • Если я обновляю существующее значение, порядок никогда не меняется, когда это необходимо.

Я видел несколько ответов, но некоторые говорят, что DataGridTextColumn не должен быть проблемой... Поэтому мне было интересно, было ли это из-за словаря... Я знаю, что первая проблема с новыми данными была как-то связана со словарем.

Объект.

    public class Player : INotifyPropertyChanged
{
    private string _id;
    private string _name;
    private int _damage;
    private int _heal;
    private int _dps;
    private int _hps;
    private int _time = 1;               

    public Player(string id, string name)
    {
        _name = name;
        _id = id;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public string Id 
    {
        get { return _id; }
        private set
        {
            _id = value;
        }
    }

    public string Name
    {
        get { return _name; }
        private set
        {                
            _name = value;                
            NotifyPropertyChanged("Name");                
        }
    }

    public int Damage
    {
        get { return _damage; }
        set
        {
            _damage = value;                
            NotifyPropertyChanged("Damage");
            Dps = _damage / _time;
        }
    }

    public int Heal
    {
        get { return _heal; }
        set
        {
            _heal = value;
            NotifyPropertyChanged("Heal");
            Hps = _heal / _time;
        }
    }

    public int Dps
    {
        get { return _dps; }
        private set
        {
            _dps = value;
            NotifyPropertyChanged("Dps");
        }
    }

    public int Hps
    {
        get {return _hps; }
        private set
        {
            _hps = value;
            NotifyPropertyChanged("Hps");
        }
    }

    public int Time
    {
        get { return _time; }
        set
        {
            _time = value;
            Dps = _damage / _time;
            Hps = _heal / _time;
        }
    }

    private void NotifyPropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }

}

ObservableCollection

public sealed class ShortList
{
    private static readonly ShortList instance =  new ShortList();
    private ObservableCollection<Player> playerList = new ObservableCollection<Player>();

    private ShortList()
    {
    }

    public static ShortList getShortList
    {
        get
        {
            return instance;
        }
    }

    public ObservableCollection<Player> getPlayerList
    {
        get
        {
            return playerList;
        }
    }

    public void updatePlayer(string id, string name, int damage, int heal, int time) 
    {
        Player player;
        player = findPlayer(id);
        if (player != null)
        {                
            player.Damage = player.Damage + damage;
            player.Heal = player.Heal + heal;
            player.Time = player.Time + time;                
        }
        else
        {                
            player = new Player(id, name);
            player.Damage = damage;
            player.Heal = heal;
            player.Time = time;
            playerList.Add(player);
        }                       
    }

    public void clear()
    {
        playerList.Clear();
    }

    private Player findPlayer(string id)
    {
        foreach (Player p in playerList)
        {
            if (p.Id == id)
            {
                return p;
            }
        }
        return null;
    }

}

XAML

<DataGrid AutoGenerateColumns="False"Name="playerDataGrid" IsReadOnly="True" ItemsSource="{Binding}">
    <DataGrid.Columns>
       <DataGridTextColumn Header="Nom" Binding="{Binding Name}" MinWidth="35"/>
       <DataGridTextColumn Header="Degats" Binding="{Binding Damage}" MinWidth="45" />
       <DataGridTextColumn Header="DPS" Binding="{Binding Dps}" MinWidth="29" />
       <DataGridTextColumn Header="Soins" Binding="{Binding Heal}" MinWidth="35" />
       <DataGridTextColumn Header="HPS" Binding="{Binding Hps}" MinWidth="29" />
   </DataGrid.Columns>
</DataGrid>

Код за окном

public partial class MiniParser : Window
{
        public MiniParser()
        {            
            InitializeComponent();
            playerDataGrid.ItemsSource = ShortList.getShortList.getPlayerList;
            temporyFill();
        }

        private void temporyFill()
        {
            ShortList.getShortList.updatePlayer("1234", "ABCD", 100, 0, 2);
            ShortList.getShortList.updatePlayer("1234", "ABCD", 100, 0, 0);
            ShortList.getShortList.updatePlayer("123", "ABC", 50, 0, 1);
            ShortList.getShortList.updatePlayer("234", "ABC", 0, 50, 1);
            ShortList.getShortList.updatePlayer("345", "BCD", 1000, 25, 25);
            ShortList.getShortList.updatePlayer("456", "CDE", 250, 0, 25);
        }

        private void startMI_Click(object sender, RoutedEventArgs e)
        {
            ShortList.getShortList.updatePlayer("5678", "BABA", 100, 100, 100);
            ShortList.getShortList.updatePlayer("1234", "ABCD", 100, 0, 0);            
        }
}

Конечно, большая часть кода предназначена для тестирования... Но идея в том, что модель обновляется, и представление должно отражать изменения (даже сортировку).

3 ответа

Решение

Я нашел решение, пока все хорошо:

Проблема заключалась в том, что ObservableCollection не вызывает событие "CollectionChanged", когда вы изменяете значение поля в объекте, который уже находится в вашем ObservableCollection. (Возможно, потому что вы не меняете ссылку на него).

Вот пример:

ObservableCollection<MyObject> myCollection = new ObservableCollection<MyObject>();
myCollection.add("CollectionChanged","Yes");
//Let's say I had a method to find a object with the first string and to change the second string
myCollection.change("CollectionChanged", "No");

Как вы можете догадаться, вторая часть, когда я изменил поле моего существующего объекта, CollectionChanged не сработала...

Итак, решение, которое я реализовал, заключается в следующем:

class ObsCollection<T> : ObservableCollection<T>
    {
        public void UpdateCollection()
        {
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(
                            NotifyCollectionChangedAction.Reset));
        }
    }

Все, что вам нужно сделать, это создать новый тип коллекции, а затем при изменении значения поля существующего объекта вызвать метод UpdateCollection().

Это решение от Wu Xuesong.

Я хотел бы сказать, что Рэйчел также оказала большую помощь.

Вы не поднимаете PropertyChanged событие, когда свойство обновляется. Это означает, что новый предмет, который имеет name изменилось с "" в "Test"или существующий элемент, который изменяется, не будет вызывать событие PropertyChanged, чтобы сообщить пользовательскому интерфейсу, что значение изменилось, и его необходимо обновить.

Чтобы исправить это, заставьте ваши свойства вызывать событие PropertyChanged при их изменении.

public class Player : INotifyPropertyChanged
{
    private string _name;
    public string Name 
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
    }

Кроме того, я перечитал ваш вопрос и понятия не имею, что вы делаете с ObservableDictionary, но я бы порекомендовал изменить это так, чтобы ваша DataGrid привязывалась непосредственно к ObservableCollection<Player>, Я подозреваю, что это часть вашей проблемы.

var playerCollection = new ObservableCollection<Player>();
playerCollection.Add(new Player { Name = "Test 1" });
playerCollection.Add(new Player { Name = "Test 2" });
playerCollection.Add(new Player { Name = "Test 3" });

playerDataGrid.ItemsSource = playerCollection;

Переместите любой код, который выглядит так:

this.NotifyPropertyChanged("somepropertyname");

в методы установки ваших свойств. Вот для чего и нужны сеттеры.

Также я второй ответ, предлагающий вам использовать ObservableCollection<T> вместо вашего ObservableDictionary<TKey,TValue>, Они очень распространены для привязок WPF.

Другие вопросы по тегам