ListView / ObservableCollection -> Keine Aktualisierung bei Änderung der Properties
-
Hallo liebes Forum,
ich wende mich nun zum ersten mal an euch, war sonst nur stiller Beobachter, aber nun weiß ich einfach nicht weiter.
Szenario: Ich habe ein MainWindow.xaml/.xaml.cs und eine eigene Klasse TaskProcess.cs. Ich möchte das Programm nutzen, um Prozesse erfassen und überwachen zu können. Dargestellt werden sollen die Prozesse in einem ListView/GridView. Die Details zu den Prozessen lese ich aus System.Diagnostics.Process aus. Mein Problem ist einfach nur, dass nach meinen Routinen wie zum Beispiel das auslesen der ProzessID, mein GridView nicht aktualisiert wird (Das Feld bleibt in der GUI leer, im Debugger ist es Datentechnisch gefüllt.).
Ich habe gefühlt schon 50 DataBinding / WPF GridView Seiten gelesen. Oftmals haben die ganz andere Probleme als ich ...Mir reicht auch schon ein Tip
Vielleicht bin ich ja auch einfach nur Blind, wer weiß ...
Hier noch mein Code:
MainWindow.xaml
<Window x:Class="StateProtocoller.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="tateProt" Height="350" Width="525"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="167*" /> <ColumnDefinition Width="143*" /> <ColumnDefinition Width="193*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="23" /> <RowDefinition Height="288*" /> </Grid.RowDefinitions> <Button Content="Überwachen" Grid.Column="2" Name="button1" Click="button1_Click" Height="23" VerticalAlignment="Top" /> <TextBox Name="textBox1" Grid.ColumnSpan="2" Height="23" VerticalAlignment="Top" /> <ListView Grid.Row="1" Grid.ColumnSpan="3" Name="ProcessGrid" KeyDown="ProcessGrid_KeyDown" Margin="0,152,0,0"> <ListView.View> <GridView AllowsColumnReorder="true" ColumnHeaderToolTip="Prozessinformationen"> <GridViewColumn DisplayMemberBinding="{Binding Path=ProcessName}" Header="ProcessName" Width="200"/> <GridViewColumn DisplayMemberBinding="{Binding Path=ProcessID}" Header="ProcessID" Width="150" /> <GridViewColumn DisplayMemberBinding="{Binding Path=KillTime}" Header="KillTime" Width="100"/> <GridViewColumn DisplayMemberBinding="{Binding Path=TotalProcessorTime}" Header="TotalProcessorTime" Width="100"/> </GridView> </ListView.View> </ListView> </Grid> </Window>
MainWindow.xaml.cs
using System; using System.Windows; using System.Windows.Input; using System.Windows.Threading; using System.Collections.ObjectModel; namespace StateProtocoller { /// <summary> /// Interaktionslogik für MainWindow.xaml /// </summary> public partial class MainWindow : Window { DispatcherTimer myTimer; ObservableCollection<TaskProcess> myProcesses = new ObservableCollection<TaskProcess>(); public ObservableCollection<TaskProcess> Processes { get { return myProcesses; } } public MainWindow() { InitializeComponent(); // Timer init this.myTimer = new DispatcherTimer(); this.myTimer.Tick += new EventHandler(myTimer_Tick); this.myTimer.Interval = new TimeSpan(0, 0, 5); this.myTimer.Start(); this.ProcessGrid.ItemsSource = this.Processes; } void myTimer_Tick(object sender, EventArgs e) { CheckTaskProcesses(); } void CheckTaskProcesses() { foreach (TaskProcess item in this.Processes) { if (!item.HaveProcess()) { item.TryToGetProcess(); } if (item.HaveProcess()) { item.ActualizeProzessorTime(); if (item.mProcess.TotalProcessorTime > item.mKillTime) { item.mProcess.Kill(); } } } } TaskProcess GetTaskProcessByName(String ProcessName) { foreach (TaskProcess item in this.Processes) { if (item.mProcessName == ProcessName) { return item; } } throw new IndexOutOfRangeException("Task not found."); } void AddTaskProcessByName(String ProcessName) { try { GetTaskProcessByName(ProcessName); } catch (Exception) { this.Processes.Add(new TaskProcess(ProcessName, new TimeSpan(0, 10, 0))); } } private void button1_Click(object sender, RoutedEventArgs e) { AddTaskProcessByName(textBox1.Text); } private void ProcessGrid_KeyDown(object sender, KeyEventArgs e) { try { if (e.Key == Key.Delete) { this.Processes.Remove(this.ProcessGrid.SelectedItem as TaskProcess); } } catch (Exception) { throw new Exception("Poof"); } } } }
TaskProcess.cs
using System; using System.Diagnostics; namespace StateProtocoller { public class TaskProcess { public String mProcessName; public String mProcessID; public TimeSpan mKillTime; public Process mProcess { get; set; } public TimeSpan mTotalProcessorTime; public TaskProcess( String ProcessName, TimeSpan KillTime) { this.mProcessName = ProcessName; this.mKillTime = KillTime; } public void ActualizeProzessorTime() { this.mTotalProcessorTime = this.mProcess.TotalProcessorTime; } public Boolean HaveProcess() { return this.mProcess != null; } public Boolean TryToGetProcess() { Process[] processes = Process.GetProcessesByName(this.mProcessName); if (processes.Length == 0) { this.mProcess = null; return false; } this.mProcess = processes[0]; this.mProcessID = this.mProcess.Id.ToString(); return true; } public String ProcessID { get { return this.mProcessID; } set { if (value != this.mProcessID) { this.mProcessID = value; } } } public String ProcessName { get { return this.mProcessName; } set { if (value != this.mProcessName) { this.mProcessName = value; } } } public TimeSpan KillTime { get { return this.mKillTime; } set { if (value != this.mKillTime) { this.mKillTime = value; } } } public TimeSpan TotalProcessorTime { get { return this.mTotalProcessorTime; } set { if (value != this.mTotalProcessorTime) { this.mTotalProcessorTime = value; } } } } }
Ich bin für alles offen, selbst wenn das bedeutet, dass ich alles neu/umschreiben muss.
Grüße
Breaker
-
Viel Code, habs nur überflogen aber:
Die GUI wird wohl nicht informiert, wenn sich Properties ändern. Versuche es mal mit der INotifyPropertyChanged-Schnittstelle.
-
Hallo µ,
Danke für den Tip.
Mir ist auch aufgefallen dass ich in der MainWindow.xaml.cs noch die alten Property-Namen drin hatte. Das habe ich nun auch geändert (Anstatt item.mProcess -> item.Process).Ich habs mal implementiert, leider ohne Erfolg. Oder ich habe an einer anderen Stelle ein Problem.
So sieht jetzt meine TaskProcess.cs aus.
using System; using System.Diagnostics; using System.ComponentModel; namespace StateProtocoller { public class TaskProcess : INotifyPropertyChanged { private String mProcessName; private String mProcessID; private TimeSpan mKillTime; private Process mProcess; private TimeSpan mTotalProcessorTime; // Event für Property Änderung public event PropertyChangedEventHandler PropertyChanged; public TaskProcess( String ProcessName, TimeSpan KillTime) { this.mProcessName = ProcessName; this.mKillTime = KillTime; } protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } public void ActualizeProzessorTime() { this.mTotalProcessorTime = this.mProcess.TotalProcessorTime; } public Boolean HaveProcess() { return this.mProcess != null; } public Process Process { get { return this.mProcess; } set { if (this.mProcess != value) { this.mProcess = value; } } } public Boolean TryToGetProcess() { Process[] processes = Process.GetProcessesByName(this.mProcessName); if (processes.Length == 0) { this.mProcess = null; return false; } this.mProcess = processes[0]; this.mProcessID = this.mProcess.Id.ToString(); return true; } public String ProcessID { get { return this.mProcessID; } set { if (value != this.mProcessID) { this.mProcessID = value; OnPropertyChanged("ProcessID"); } } } public String ProcessName { get { return this.mProcessName; } set { if (value != this.mProcessName) { this.mProcessName = value; OnPropertyChanged("ProcessName"); } } } public TimeSpan KillTime { get { return this.mKillTime; } set { if (value != this.mKillTime) { this.mKillTime = value; OnPropertyChanged("KillTime"); } } } public TimeSpan TotalProcessorTime { get { return this.mTotalProcessorTime; } set { if (value != this.mTotalProcessorTime) { this.mTotalProcessorTime = value; OnPropertyChanged("TotalProcessorTime"); } } } } }
Ich weiss es ist viel Quellcode auf einmal, aber ich will euch keine Informationen vorenthalten
Grüße
Breaker
-
Nachtrag:
Ich habe jetzt auch mal einen Haltepunkt auf die Methode:
protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }
gesetzt.
Die wird nie ausgeführt. Was mich auch wundert, woher soll PropertyChanged != null sein? Das wird doch nie irgendwo erstellt. Oder irre ich da?
Grüße
Breaker
-
Die kann auch nur ausgeführt werden, wenn jemand die Properties setzt.
Du hast aber z.B. this.mProcessID = this.mProcess.Id.ToString(); da stehen. Das läuft am Property vorbei, es wird kein Event ausgelöst und die GUI nicht über eine Änderung informiert.
Nachtrag:
"woher soll PropertyChanged != null sein?"
Datengebundene Steuerelemente hängen sich da selbst ein.
-
µ,
ich liebe dich!
Vielen Dank. Nun funktioniert es! JippieIch habe testweise einfach mal folgendes gemacht:
this.mProcessID = this.mProcess.Id.ToString(); OnPropertyChanged("ProcessID");
Und er hat es sofort in der GUI angezeigt.
Na dann kanns ja los gehen!
Vielen Dank und Grüße
Breaker
-
Breaker schrieb:
µ,
ich liebe dich!Oh Gott schnell weg hier
-
Breaker schrieb:
Ich habe testweise einfach mal folgendes gemacht:
Warum verwendest du nicht in TryToGetProcess die Properties wenn du willst das OnPropertyChanged aufgerufen wird?
P.S: ObservableCollection leitet nur die Änderungen weiter, die die Liste selbst und nicht deren Elemente betreffen (Sprich Einfügen/Löschen...). Eine andere Umsetzung würde den Einsatz einschränken.
-
"Ich habe testweise einfach mal folgendes gemacht:"