StatusBar TextBox zur Laufzeit im Programm ändern

  • WPF

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von Nofear23m.

    StatusBar TextBox zur Laufzeit im Programm ändern

    Hallo zusammen,

    ich brech mir jedesmal die Finger, wenn ich versuche mit WPF klar zu kommen. Ich bin wirklich irgendwie zu Blöd dafür.
    Ich schreibe gerade eine Anwendung, die XML Knoten einer bestimmten Ausgabe von Fremdprogrammen prüft und weiterverarbeitet.
    Die Anwendung kommt ohne Datenbankfunktionalitäten aus. Ich benötige lediglich drei UserControls, die in das MainForm eingebunden werden sollen und ein paar Buttons.
    Bis hierher habe ich noch kein Problem!

    Mein Problem ist eine einfache Statusleiste im MainForm, deren Textbox und später auch ProgressBar soll von verschiedenen Funktionen angesprochen und verändert werden.
    Da scheitere ich aber kläglich. Ich habe mir jetzt mehrere Tage lang die unterschiedlichsten Beispiele im Internet angesehen und versucht anzuwenden, es ist mir nie gelungen.

    Ich habe eine Klasse "SBar" mit INotifyPropertyChanged implementiert und einen LabelText vordefiniert.
    In der XAML vom MainForm habe ich mit dem Picker den DataContext "SBar" zugefügt und die Textbox zeigt diesen "Initialwert" auch an.

    Nun bringe ich es aber nicht hin, dass die Textbox aktualisiert wird.

    Hier meine Klasse SBar:
    Spoiler anzeigen

    C#-Quellcode

    1. public class SBar : INotifyPropertyChanged
    2. {
    3. private string labelText = "Initialwert";
    4. public string LabelText
    5. {
    6. get { return labelText; }
    7. set
    8. {
    9. labelText = value;
    10. NotifyPropertyChanged("LabelText");
    11. }
    12. }
    13. public event PropertyChangedEventHandler PropertyChanged;
    14. private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    15. {
    16. if (PropertyChanged != null)
    17. {
    18. PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    19. }
    20. }
    21. private void ModifyLabelText(string val)
    22. {
    23. labelText = val;
    24. }
    25. public static SBar CreateLabelText(string val)
    26. {
    27. var StatusText = new SBar();
    28. StatusText.ModifyLabelText(val);
    29. return new SBar();
    30. }
    31. }



    In der MainWindow.xaml.cs habe ich den Aufruf im ersten Schritt erweitert.

    C#-Quellcode

    1. public MainWindow()
    2. {
    3. InitializeComponent();
    4. SBar.CreateLabelText("Fertig");
    5. }


    Das hier ist nur ein TestProjekt, mit dem ich mein Problem nachgestellt habe.
    Ich hänge das Projekt hier mit an.

    Es wäre schön, wenn mir jemand auf die Sprünge helfen könnte.
    StatusbarText.zip
    Hallo

    Dksksm schrieb:

    Ich bin wirklich irgendwie zu Blöd dafür.

    Nene, das wird schon, was Binding betrifft musst du nur wissen wie es funktioniert. Hierfür kann ich dir mein Tutorial empfehlen. Das Kapitel Binding ist auch bereits fertig. Schau dir das mal an.

    Dksksm schrieb:

    Ich habe eine Klasse "SBar" mit INotifyPropertyChanged implementiert und einen LabelText vordefiniert.

    Das ist auch gut so, ich habe gesehen das du auch <CallerMemberName> implementiert hast. Es reicht also im Setter einfach ein NotifyPropertyChanged();.

    Dksksm schrieb:

    Nun bringe ich es aber nicht hin, dass die Textbox aktualisiert wird.

    Naja, zum einen erstellst du im XAML eine eigene Instanz der Klasse SBar indem du den DatenContext hier festlegst. Die WPF ruft hierdurch den Parameterlosen Konstruktor auf. Da es keinen in SBar gibt wird es auch angemeckert.

    Desweiteren gibst du mit return new SBar(); immer beim setzen des Werts eine neue Instanz der Klasse zurück, worauf soll die WPF also binden.

    Du erstellst am einfachsten ein Property im MainWindow (nicht MainForm) und Bindest die View auf die MainWindow Instanz.
    Dann kannst du auf dieses Property binden.

    C#-Quellcode

    1. public MainWindow()
    2. {
    3. InitializeComponent();
    4. MySBar = new SBar();
    5. this.DataContext = this;
    6. MySBar.LabelText = "Fertig";
    7. }
    8. private SBar _mySBar;
    9. public SBar MySBar
    10. {
    11. get { return _mySBar; }
    12. set { _mySBar = value; }
    13. }


    Durch das this.DataContext = this; Bindest du die View an das MainWindow. Nun musst du im MainWindow nur noch binden:

    XML-Quellcode

    1. <TextBox x:Name="SText" Text="{Binding MySBar.LabelText, Mode=OneWay}" BorderBrush="Crimson" BorderThickness="0" IsReadOnly="True" Margin="6,0,3,0" />


    Grüße
    Sascha
    Dateien
    • StatusbarText.zip

      (210,63 kB, 190 mal heruntergeladen, zuletzt: )
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo @Nofear23m, danke für deine Antwort.

    Ich arbeite auch mit deinem Tutorial und wäre ohne dem gar nicht erst soweit gekommen. Mein Lob an Dich und auch meinen Dank dafür.

    In der Tat hatte ich schon ein ähnliches Konstrukt, damit konnte ich bereits Textänderungen an der Status-TextBox vornehmen, allerdings eben nur aus MainWindow heraus.
    Ich dachte es wäre der falsche Weg, weil mir geht es ja darum, dass ich den Text auch aus den UserControls heraus ändern will, am liebsten sogar direkt aus anderen Klassen, die als "Worker" dienen.
    Ein weiteres zukünftiges Ziel soll ja auch sein, den Fortschrittbalken ebenfalls auf diese Weise anzusprechen.

    Deshalb hatte ich die Funktion mit dem Getter/Setter wieder zurück in die Klasse SBar verfrachtet. Mein Problem, nämlich den Wert des Textes von "Ausserhalb" zu verändern hat sich nicht verändert.
    In einem Usercontrol verarbeite ich eine extrem große XML Struktur mit XMLReader.ReadToDescendant und XMLReader.ReadToNextSibling häppchenweise und will Hinweistexte auf diese Textbox ausgeben.

    Ohne diese Funktionalität sehe ich einfach keinen Sinn darin mit WPF weiterzumachen. Mit WinForm wäre das Programm längst fertig gestellt. Ich hatte lediglich die Hoffnung, mit dem wenig anspruchsvollen Programm den Einstieg in WPF zu schaffen. Allein schon zu sehen wie das Command Binding funktioniert, begeistert mich. Aber mit diesem Problem komme ich einfach nicht weiter.

    Viele Grüße und einen schönen Sonntag weiterhin.
    Na dann mach mal das Projekt wie du glaubst und versuche zu binden. Stell dir die View vor und bilde dies in einer Klasse ab.

    Wenn du nicht weiterkommst meldest du dich einfach. Versteht man Binding ist es einfach. Es muss nur klick machen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Würde ich es hinkriegen hätte ich nicht um Hilfe ersucht. Ich komme einfach nur nicht weiter. ich weiß eben nicht, wie ich die Funktion aufrufen kann. Aus MainWindow geht es ja, wie deine Funktion beweist.
    Aber wie rufe ich das aus einer anderen Klasse auf ohne "new"? Daran verzweifle ich, und das schon seit Tagen.
    Hallo

    In so einem Fall ist es am besten man übergbibt die Instanz der Statusbar aus der darüberliegenden Klasse an die "Worker" Klasse.

    Ich hab dir mal ein Beispiel gemacht.

    Grüße
    Sascha
    Dateien
    • StatusBarDemo.zip

      (231,15 kB, 187 mal heruntergeladen, zuletzt: )
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo Sascha,

    danke für das Beispiel. Ich habe es nach C# portiert und versucht mein "Projekt" da drinnen abzubilden.
    Ich bekomme es schon mal hin, den DesignTime Text im Aufruf des MainViewModel's zu ersetzen.
    Aber dann weiß ich wieder nicht weiter...
    Ich habe das UC ucTest implementiert und dort ebenfalls das MainViewModel eingebunden.
    Der Button Command wird auch ausgeführt (MessageBox) aber der neue StatusText wird nicht gesetzt.

    Ich schnalle einfach nicht, wie man das mittels Binding lösen kann. Ich möchte nichts anderes, als in dem StatusBarItem TextBox Texthinweise ausgeben.
    Wenn das so schwer zu lösen ist, was wäre denn ein WPF üblicher Weg?

    Grüße
    Rob

    StatusbarText.zip

    Dksksm schrieb:

    Ich habe das UC ucTest implementiert und dort ebenfalls das MainViewModel eingebunden

    Warum das denn?

    Das ganze Fenster ist das MainViewModel. Der untere Teil (Statusbar) ist der Teil der an die Eigenschaft "Status" INNERHALB des MainViewModels gebunden ist.
    Der obere Teil ist der Teil der an die Eigenschaft Worker gebunden.

    Insofern muss doch dein UCL an "Worker" gebunden werden.

    Dksksm schrieb:

    Wenn das so schwer zu lösen ist, was wäre denn ein WPF üblicher Weg?

    Das ist der WPF übliche Weg.

    Ich war so frei und habe dir mal ne Grafik gemacht:

    ROT = MainViewModel
    Blau = WorkerViewModel
    Grün = StatusBarViewModel



    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo

    Nun gut, warum setzt du im XAML des UserControls denn den DatenKontext auf eine neue Instanz eines WorkerViewModel?

    XML-Quellcode

    1. <UserControl.DataContext>
    2. <viewmodel:WorkerViewModel/>
    3. </UserControl.DataContext>

    Damit Bindest du nicht auf das Worker Propertie der Klasse MainViewModel wie du ja solltest sondern erstellst eine neue Instanz der Klasse WorkerViewModel!!!
    Das kommentierst du mal aus.

    Im MainWindow nimmst du nun mal das UserControl raus. Warum ein UserControl in einem leeren UserControl? Doppelt gemoppelt.
    Und dann setzt du den Datenkontext des UserControls auf das Propertie Worker. Und schon ist alles gut.

    Also wird aus:

    XML-Quellcode

    1. <UserControl Grid.Row="2">
    2. <local:ucTest Height="Auto" Margin="0,0,0,0" Width="Auto"/>
    3. </UserControl>


    nun mehr:

    XML-Quellcode

    1. <local:ucTest Grid.Row="2" DataContext="{Binding Worker}" Height="Auto" Margin="0,0,0,0" Width="Auto"/>


    Das wars dann auch schon.

    Merke. Setzt du im XAML den DataContext wird dadurch eine NEUE Instanzt der Klasse erstellt indem von der WPF der Parameterlose Konstruktor aufgerufen wird. Alles was in der Hirarchie dann darunter ist darf nur gebunden werden da sonst auch hier eine neue Instanz erstellt wird anstatt auf eine Instanz zu Binden.

    Aber das wird schon, wirst sehen. Kapiert man es erstmal ist es super easy. Nur mut.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo Sascha,

    erst einmal DANKE, es funktioniert.
    Ohne deine Berichtigung des XML-Quellcodes hätte ich die Erklärung im Leben nicht verstanden. Vieles muss man (ich wenigstens) sehen, damit ich es schnalle. Aber das AHA-Erlebnis kam als ich die Berichtigung sah und auch verstand :)
    Wenn man weiß wie, ist mit WPF vieles einfacher, aber die Hürde ist sehr, sehr hoch.

    Punkt 1, dass ich im XAML des UC eine neue Instanz des WorkerViewModels drauf setze, darauf wäre ich auch im Leben nicht gekommen.

    Danke für Deine Hilfe, ich denke ich komme damit eine große Strecker weiter.
    Ich hoffe, es dient auch anderen.

    Viele Grüße
    Rob
    Hallo

    Das freut mich.

    Ja, das ist das was ich immer versuche zu vermitteln. WPF kann man nicht mal schnell "probieren". Das muss man lernen. Speziell das Bindingsystem mit all seinen vorzügen, aber auch hürden.
    Das geht es dann weiter mit Dependency Properties, Convertern, Attached Properties usw.

    Deshalb... entweder man will es lernen und versucht es zu verstehen - auch durch viel probieren und versuchen - oder man bleibt bei WinForms.
    Die WPF ist mächtig und gut, will aber verstanden werden. Vorallem wenn man aus der Welt von WinForms kommt ist es sicher schwer sich von der CodeBehind zu verabschieden.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##