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.