Benutzer mit den meisten Antworten
C# | WPF : ListBox "Step by Step" befüllen als Logging

Frage
-
Hallo Zusammen,
ich bin aktuell dabei eine kleine GUI zu schreiben, welche als Log-Ausgabe eine ListBox erhalten soll.
Mein Ziel ist es hierbei, dass diese GUI mehrer aktionen ausführt und dann nach jeder Funktion bzw. bei jedem Arbeits-Step einen neuen Eintrag in die ListBox hinzufügt, so dass man immer auf den aktuellen Stand ist, was das Tool eigentlich macht.
Mein Problem ist es hierbei aber, dass er die Listbox nicht step bei Step befüllt sondern wartet, bis alles abegschlossen ist und dann die ListBox auf Einmal befüllt.
Im Internet habe ich schon viele Einträge gefunden, wo dann steht man sollte nach jedem Befüllen die ListBox items Refreshen, allerdings ergibt das ganze bei mir keinen Erfolg.
Hoffe ihr könnt mir helfen:S
Schon einmal vielen DAnk!
Gruß
Tux
Antworten
-
Hallo,
meine Vorredner haben schon die wichtigen Stichpunkte für den theoretischen Teil genannt. Da du noch Probleme hast, möchte ich dir ein Beispiel geben, wie es funktioniert.Mein nachfolgendes Beispiel zeigt auch nur Ansätze wie man es machen kann - der beste Programmierstil ist es nicht.
Zunächst empfehle ich dir eine Klasse anzulegen um ein Log-Item darstellen zu können:
public class LogItem : INotifyPropertyChanged { private string _Notification; public string Notification { get { return _Notification; } set { if (_Notification != value) { _Notification = value; OnPropertyChanged(); } } } private LogItemImportance _Importance; public LogItemImportance Importance { get { return _Importance; } set { if (_Importance != value) { _Importance = value; OnPropertyChanged(); } } } #region INotifyPropertyChanged Member private void OnPropertyChanged([CallerMemberName] string propertyName = "") { var evt = this.PropertyChanged; if (evt != null) evt(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; #endregion } public enum LogItemImportance { Height, Middle, Low, }
Im DataContext, nachfolgend einfach das Window, musst du nun eine Liste davon anlegen:
public partial class MainWindow : Window { public MainWindow() { this.LogItems = new ObservableCollection<LogItem>(); this.DataContext = this; InitializeComponent(); } public ObservableCollection<LogItem> LogItems { get; set; }
Die Klasse implementiert INotifyPropertyChanged und das Window nutzt eine ObservableCollection<T> um die GUI über Änderungen einer Eigenschaft bzw. der Liste zu informieren.
Im XAML kannst du dann an die Liste binden:
<ListBox ItemsSource="{Binding LogItems}"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Notification}"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
In diesem Fall wird einfach von jedem Eintrag die Notification-Eigenschaft ausgegeben. Über einen ValueConverter könntest du auch noch die Wichtigkeit in Form einer Farbe darstellen etc.
Wenn du nun kontinuierlich Einträge anzeigen willst, brauchst du einen 2. Thread. Am einfachsten geht das mittlerweile mit async und await:
//Async kennzeichnet den Eventhandler (Button-Click o.ä.) als Asynchron private async void Window_Loaded(object sender, RoutedEventArgs e) { await Task.Run(async () => { //Neuen Thread starten //Invoke ist bei Zugriffen auf die GUI bzw. in der GUI gebundene Listen notwendig Dispatcher.Invoke(() => LogItems.Add(new LogItem() { Notification = "A", Importance = LogItemImportance.Low })); await Task.Delay(1000);//1s warten Dispatcher.Invoke(() => LogItems.Add(new LogItem() { Notification = "B", Importance = LogItemImportance.Middle })); await Task.Delay(1000);//1s warten Dispatcher.Invoke(() => LogItems.Add(new LogItem() { Notification = "C", Importance = LogItemImportance.Height })); await Task.Delay(1000);//1s warten Dispatcher.Invoke(() => LogItems.Add(new LogItem() { Notification = "D", Importance = LogItemImportance.Middle })); }); }
Das ist nur eines von vielen Beispiel, wie man es machen kann. Ich kann nicht alles an dem Code erklären - dafür ist er schon zu komplex und ich kenne deinen wirklichen Wissensstand nicht. Du kannst aber gerne Rückfragen stellen.
Wenn du noch Probleme bei der Umsetzung hast, wäre es nicht schlecht, wenn du mal deinen Code zeigst.
Tom Lambert - .NET (C#) MVP
Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets- Als Antwort vorgeschlagen Claudius Hauser Montag, 9. März 2015 15:34
- Als Antwort markiert Aleksander Chalabashiev Montag, 16. März 2015 13:35
Alle Antworten
-
Hi Tux,
wenn alle Arbeiten in einem thread ausgeführt werden und dabei die CPU zu 100% ausgelastet wird, dann ist es wirklich so, dass asynchrone Prozesse wie das Aktualisieren der Oberfläche u.U. sehr spät ausgeführt werden können. Wenn jedoch der thread regelmäßig wartet, dann kann man über INotifyPropertyChanged die Oberfläche informieren, dass sie sich die anzuzeigenden Daten selbst holt. Wenn der Prozess jedoch zu 100% die CPU auslastet, dann sollte man über multi-threading nachdenken und den Vordergrund-thread immer auf Ereignisse warten lassen. In diesem Fall kann die Oberfläche zeitnah aktualisiert werden.--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks -
-
Kenne mich hier leider noch nicht so gut aus. habe nun einen Test mit ObservableCollection gemacht, leider habe ich hier das gleiche Problem das mein komplettes "Log" auf einmal angezeigt wird und nicht Step by Step.
Wenn ich nach jedem Logeintrag allerdings eine MessageBox oder so anzeige dann funktioniert das zeilenweise Loggen :/
-
Hallo,
meine Vorredner haben schon die wichtigen Stichpunkte für den theoretischen Teil genannt. Da du noch Probleme hast, möchte ich dir ein Beispiel geben, wie es funktioniert.Mein nachfolgendes Beispiel zeigt auch nur Ansätze wie man es machen kann - der beste Programmierstil ist es nicht.
Zunächst empfehle ich dir eine Klasse anzulegen um ein Log-Item darstellen zu können:
public class LogItem : INotifyPropertyChanged { private string _Notification; public string Notification { get { return _Notification; } set { if (_Notification != value) { _Notification = value; OnPropertyChanged(); } } } private LogItemImportance _Importance; public LogItemImportance Importance { get { return _Importance; } set { if (_Importance != value) { _Importance = value; OnPropertyChanged(); } } } #region INotifyPropertyChanged Member private void OnPropertyChanged([CallerMemberName] string propertyName = "") { var evt = this.PropertyChanged; if (evt != null) evt(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; #endregion } public enum LogItemImportance { Height, Middle, Low, }
Im DataContext, nachfolgend einfach das Window, musst du nun eine Liste davon anlegen:
public partial class MainWindow : Window { public MainWindow() { this.LogItems = new ObservableCollection<LogItem>(); this.DataContext = this; InitializeComponent(); } public ObservableCollection<LogItem> LogItems { get; set; }
Die Klasse implementiert INotifyPropertyChanged und das Window nutzt eine ObservableCollection<T> um die GUI über Änderungen einer Eigenschaft bzw. der Liste zu informieren.
Im XAML kannst du dann an die Liste binden:
<ListBox ItemsSource="{Binding LogItems}"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Notification}"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
In diesem Fall wird einfach von jedem Eintrag die Notification-Eigenschaft ausgegeben. Über einen ValueConverter könntest du auch noch die Wichtigkeit in Form einer Farbe darstellen etc.
Wenn du nun kontinuierlich Einträge anzeigen willst, brauchst du einen 2. Thread. Am einfachsten geht das mittlerweile mit async und await:
//Async kennzeichnet den Eventhandler (Button-Click o.ä.) als Asynchron private async void Window_Loaded(object sender, RoutedEventArgs e) { await Task.Run(async () => { //Neuen Thread starten //Invoke ist bei Zugriffen auf die GUI bzw. in der GUI gebundene Listen notwendig Dispatcher.Invoke(() => LogItems.Add(new LogItem() { Notification = "A", Importance = LogItemImportance.Low })); await Task.Delay(1000);//1s warten Dispatcher.Invoke(() => LogItems.Add(new LogItem() { Notification = "B", Importance = LogItemImportance.Middle })); await Task.Delay(1000);//1s warten Dispatcher.Invoke(() => LogItems.Add(new LogItem() { Notification = "C", Importance = LogItemImportance.Height })); await Task.Delay(1000);//1s warten Dispatcher.Invoke(() => LogItems.Add(new LogItem() { Notification = "D", Importance = LogItemImportance.Middle })); }); }
Das ist nur eines von vielen Beispiel, wie man es machen kann. Ich kann nicht alles an dem Code erklären - dafür ist er schon zu komplex und ich kenne deinen wirklichen Wissensstand nicht. Du kannst aber gerne Rückfragen stellen.
Wenn du noch Probleme bei der Umsetzung hast, wäre es nicht schlecht, wenn du mal deinen Code zeigst.
Tom Lambert - .NET (C#) MVP
Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets- Als Antwort vorgeschlagen Claudius Hauser Montag, 9. März 2015 15:34
- Als Antwort markiert Aleksander Chalabashiev Montag, 16. März 2015 13:35