Laden...

WPF DataGrid - Refresh single Item

Erstellt von ChristianAT vor 3 Jahren Letzter Beitrag vor 3 Jahren 1.219 Views
C
ChristianAT Themenstarter:in
2 Beiträge seit 2021
vor 3 Jahren
WPF DataGrid - Refresh single Item

Hallo Community,

Ich habe ein WPF DataGrid das ich mit einer ObservableCollection<T> fülle.
Die Daten bekomme ich von einem WCF Service.


dgMitarbeiter.ItemsSource = new ObservableCollection<Mitarbeiter>(_wcfService.MitarbeiterGet());

Wenn ich nun Änderungen an einem Objekt per DataGrid mache, gebe ich das geändert Objekt zurück an den WCF Service.
Der WCF Service macht dann Berechnungen etc. und ändert das Objekt weiter ab (Properties werden gesetzt etc.)


_wcfService.MitarbeiterUpdate(dgMitarbeiter.SelecteItem as Mitarbeiter);

Nun möchte ich alle Änderungen die der WCF Service gemacht hat in meinem DataGrid sehen.
Ich möchte aber nicht ItemsSource nochmal setzten, da die Liste riesig ist und die Selektierungen, expanded rowDetails, sortierung, focus etc. verloren geht.
Das alles zu speichern und nach dem refresh wieder zu setzten kann nicht die Lösung sein.

Mein erfolgloser Versuch, das geänderte Objekt zu übernehmen:


((ObservableCollection<Mitarbeiter>)(dgMitarbeiter.ItemsSource))[ix] = _wfcService.MitarbeiterGetById(id);

Wenn ich das so mache, sehe ich zwar die Änderungen am DataGrid jedoch geht das Binding für ein weiteres bearbeiten komplett verloren.
Hat jemand eine Idee oder einen Tip für mein Vorhaben?

Danke

4.931 Beiträge seit 2008
vor 3 Jahren

Hallo und willkommen,

warum arbeitest du nicht mit [Artikel] MVVM und DataBinding?

Dann würdest du im ViewModel direkt eine Eigenschaft vom Typ ObservableCollection<Mitarbeiter> halten und nach Änderung des einen Eintrags PropertyChanged(...) aufrufen (dazu muß jedoch auch die Klasse Mitarbeiter das Interface INotfiyPropertyChanged implementieren, evtl. brauchst du dazu dann eine ViewModel-spezifische Mitarbeiter-Klasse).

C
ChristianAT Themenstarter:in
2 Beiträge seit 2021
vor 3 Jahren

Danke für den Link und deine Hilfe.
Aber auch wenn MVVM eingehalten wird habe ich doch das selbe Problem, oder verstehe ich da was falsch?
Ich habe ein kleines MVVM beispiel aufgebaut, jedoch mit dem selben Ergebnis:

XAML:


	<DataGrid ColumnWidth="auto" 
			AutoGenerateColumns="False" 
			IsReadOnly="False" 
			CanUserAddRows="True" 
			CanUserDeleteRows="True" 
			SelectionMode="Single" 
			RowDetailsVisibilityMode="Collapsed" 
			CellStyle="{StaticResource DataGridRowVerticalCenter}" 
			ItemsSource="{Binding Path=Mitarbeiter}"
			SelectedItem="{Binding Path=SelectedMitarbeiter}"
			RowEditEnding="DataGrid_RowEditEnding">
  .............

code behind:


public partial class MitarbeiterPage : Page
	{
		public MitarbeiterPage()
		{
			InitializeComponent();
			this.DataContext = new MitarbeiterViewModel();
		}

		private void DataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
		{
			(this.DataContext as MitarbeiterViewModel).Update();
		}
}

Da RowEditEnding nicht an ein Command gebunden werden kann, rufe ich im Event die Update Methode vom ViewModel auf.

ViewModel:



public class MitarbeiterViewModel : BaseViewModel
	{
		WCFManagerServiceClient _wcfService;
		public MitarbeiterViewModel()
		{
			_wcfService = new WCFManagerServiceClient();
            UpdateCommand = new RelayCommand(Update);
			LoadData();
		}

		private ObservableCollection<Mitarbeiter> _mitarbeiter;
		public ObservableCollection<Mitarbeiter> Mitarbeiter 
		{
			get { return _mitarbeiter; }
			set 
			{
				_mitarbeiter = value;
				OnPropertyChanged("Mitarbeiter");
			}
		}
		private void LoadData()
		{
			Mitarbeiter = new ObservableCollection<Mitarbeiter>(_wcfService.MitarbeiterGet());
		}

		private Mitarbeiter _selectedMitarbeiter;
		public Mitarbeiter SelectedMitarbeiter 
		{
			get { return _selectedMitarbeiter; }
			set 
			{
				_selectedMitarbeiter = value; 
				OnPropertyChanged("SelectedMitarbeiter"); 
			}
		}

		#region Commands
		public RelayCommand UpdateCommand { get; }

		public void Update()
		{
			try 
			{
				_wcfService.MitarbeiterUpdate(SelectedMitarbeiter);
				LoadData();
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}
		#endregion
	}	


Wenn ich nach dem Update mit LoadData() die Daten aktualisiere, weil der WCF Service einige Daten ergänzt hat, ist das gesamte DataGrid zurückgesetzt.
Sprich kein Focus, Sortierung, RowDetails nicht mehr expanded etc.
Auch wenn ich nur das Objekt in der Property Mitarbeiter aktualsiere verhälts sich das DataGrid "unschön".

D
161 Beiträge seit 2017
vor 3 Jahren

Zu den Methodenaufruf


OnPropertyChanged()

würde ich keine magic strings nehmen, sondern


nameof(PropertyName)

309 Beiträge seit 2020
vor 3 Jahren

Wenn ich nach dem Update mit LoadData() die Daten aktualisiere, weil der WCF Service einige Daten ergänzt hat, ist das gesamte DataGrid zurückgesetzt.
Sprich kein Focus, Sortierung, RowDetails nicht mehr expanded etc.
Auch wenn ich nur das Objekt in der Property Mitarbeiter aktualsiere verhälts sich das DataGrid "unschön".

Mit der Model-Liste, die du vom WCF Service bekommst, aktualisierst du einfach die ViewModel-Liste (ein ViewModel des Models, nicht wie du es gemacht hast). So könntest du prüfen ob sich an einzelnen Items was verändert hat, und wenn ja, dann aktualisieren.

16.806 Beiträge seit 2008
vor 3 Jahren

_wcfService.MitarbeiterUpdate(SelectedMitarbeiter);

Damit übergibst Du UI-Logik an eine Service-Klasse.
Das ist prinzipiell genau anders rum, wie man es tun sollte.

Der Service sollte eine Liste zurück geben, die Du in der UI bindest.

Im Endeffekt kann man das zusammen mit Reactive Extensions noch weiter perfektionieren; aber denke, dass Du das erst angehen solltest, wenn Du MVVM verstanden hast.
https://oz-code.com/blog/net-c-tips/reactive-wpf-part-1-introduction-to-reactive-extensions

4.931 Beiträge seit 2008
vor 3 Jahren

@ChristianAT. Du hast meinen eingeklammerten Satz noch nicht beachtet. Nimm dir ein Beispiel an dem Code in C# WPF & MVVM | update data of a property with a binding to the object (bei dir dann die Klasse Mitarbeiter statt Student).
Und statt dem Neuladen aller Daten, einfach die passenden Eigenschaften des Mitarbeiter-Objekts ändern (und dieses ruft dann PropertyChanged auf!).

Wie auch JimStark geschrieben hat, wenn Mitarbeiter bei dir bisher eine reine Modelklasse ist, dann erzeuge eine weitere ViewModel-spezifische Klasse und verwende diese dann (d.h. kopiere die Daten aus dem Model in das VM-Datenobjekt).

PS: Auch die Verwendung von [CallerMemberName] beim OnPropertyChanged-Aufruf vereinfacht den Code.