WPF Wert einer Textbox



  • Sorry aber deine Antwort ist etwas verwirrend.

    Das Binding wird den Wert der TextBox gleich in den Namen des selektierten Rezeptes reinschreiben, wenn der Focus verloren geht. Wenn du das nicht willst, musst du dies halt im Hintergrund im ViewModel regeln.

    Das kann ich ja mit einem Oneway Binding machen. Oder was muss ich da im ViewModel regeln. Aber das war ja gar nicht meine Frage.

    Binde das Text-Property der TextBox an ein Property im ViewModel

    Aber die Text-Property ist ja schon gebunden. Wie kann ich die noch an eine andere Property binden? Das war ja die eigentliche Frage.


  • Administrator

    1. Absatz beschreibt das aktuelle Verhalten. Ich weiss nicht, ob dir das bewusst war. Der letzte Satz dient als Übergang zum 2. Absatz.
    2. Absatz beschreibt das Vorgehen der Lösung. Du bindest das Text -Property nur einmal an das Property im ViewModel.

    class TheUltimateViewModel
      : INotifyPropertyChange
    {
      public event PropertyChangedEventHandler PropertyChanged;
    
      private ObserverableCollection<Recipe> m_recipes;
    
      public string CurrentRecipeName
      {
        get
        {
          var currentRecipe = GetCurrentRecipe();
          return currentRecipe != null ? currentRecipe.Name : String.Empty;
        }
        set
        {
          var currentRecipe = GetCurrentRecipe();
    
          if(currentRecipe != null)
          {
            currentRecipe.Name = value;
            RaisePropertyChanged("CurrentRecipeName");
          }
    
          /*
          Du kannst hier natürlich auch noch anderes machen.
          Z.B. könntest du die Daten auch zwischenspeichern oder
          falls kein aktuelles Rezept ausgewählt ist, ein neues 
          mit dem gegebenen Namen auswählen. ICollectionView
          bietet auch Möglichkeiten das CurrentItem zu verändern,
          siehe dazu die MoveCurrentTo Methoden.
          */
        }
      }
    
      public IEnumerable<Recipe> Recipes
      {
        get { return m_recipes; }
      }
    
      public TheUltimateViewModel()
      {
        m_recipes = new ObservableCollection<Recipe>();
    
        GetRecipeCollectionView().CurrentChanged += this.OnCurrentRecipeChanged;
      }
    
      private void OnCurrentRecipeChanged(object source, EventArgs e)
      {
        RaisePropertyChanged("CurrentRecipeName");
      }
    
      private void RaisePropertyChanged(string propertyName)
      {
        var handler = PropertyChanged;
    
        if(handler != null)
        {
          handler(new PropertyChangedEventArgs(propertyName));
        }
      }
    
      private ICollectionView GetRecipeCollectionView()
      {
        return (ICollectionView)CollectionViewSource.GetDefaultView(this.Recipes);
      }
    
      private Recipe GetCurrentRecipe()
      {
        return (Recipe)GetRecipeCollectionView().CurrentItem;
      }
    }
    
    <ListView DisplayMemberPath="Name"
              IsSynchronizedWithCurrentItem="True"
              ItemsSource="{Binding Recipes}" />
    <!-- ... -->
    <TextBox Text="{Binding CurrentRecipeName}" />
    

    Jetzt besser klar?

    Grüssli



  • Ich stelle das mal plastisch dar.

    Listview -> Textbox -> Property (in ViewModel)

    Der Wert der in der ListView ausgewählt ist soll automatisch in die Textbox wandern. Der Wert der Textbox soll in die Property im ViewModel wandern wenn auf einen Button geklickt wird.

    Und das sozusagen alles OneWay. Dazu müsste ich ja nun 2 Werte an die Textbox binden.



  • Sorry war wohl etwas zu schnell mit meinem letzten Beitrag. Hat sich wohl überschnitten. Habe deinen noch gar nicht gelesen. Schau mir das mal an und melde mich wieder



  • So jetzt habe ich mir das angeschaut. Und versteh nun auch deinen ersten Beitrag :-).

    Aber kompliziert und aufwendig ist das trotzdem. Das wäre doch viel einfacher wenn man die Textbox einfach an unterschiedliche Propertys binden könnte mit unterschiedlichen Triggern. Aber geht wohl nicht 😞


  • Administrator

    Naja, du musst das Zeug ja irgendwo zwischenspeichern, damit du es später per Command weiterverarbeiten kannst. Daher muss du sowieso ein Property im Hintergrund haben.

    Zudem ist INotifyPropertyChange so üblich bei ViewModels, dass ich mir persönlich eine Basisklasse ObservableObject erstellt habe, wo all diese Funktionalität einfach gekapselt ist. Alle meine ViewModels erben von dieser Klasse. Zudem habe ich Hilfsklassen für ICollectionView / CollectionViewSource , weil ich dies ebenfalls sehr oft verwende. Dadurch reduziert sich das bei mir auf ein paar Zeilen Code und ist überhaupt nicht kompliziert 😉

    Theoretisch könnte man vielleicht noch etwas über ein MultiBinding machen. Damit kannst du tatsächlich mehrere Properties an ein einzelnes binden. Du brauchst dazu aber auch eine Klasse, welche das Interface IMultiValueConverter implementiert. Viel einfacher wird das nicht und du hast sogar eher weniger Kontrolle. Ist eigentlich auch nicht für den Fall gedacht, den du hier hast.

    Grüssli



  • Klar brauche ich ein Property im Hintergrund das ist klar.

    Ich wollte einfach an die Textbox 2 Propertys binden. Einmal dass ich den Wert bekomme von der Listview. Also die Quelle. Und auch an die Property im Hintergrund wohin ich den Wert dann wieder schreibe. Also das target.

    Das mit dem INotifyPropertyChange usw. ist nicht das Thema. Da nutze ich inzwischen ein Framework: Catel da bin ich eigentlich sehr zufrieden mit.



  • Das mit dem MultiBinding habe ich auch schon getestet.

    In der ConvertMethode erhalte ich ein Array der Werte. Da könnte ich mir ja den entsprechenden aussuchen und zurück geben.

    Beim ConvertBack wird es schwieriger. Da erhalte ich den Wert der Textbox und muss ein Array zurückgeben. Sozusagen müsste ich nur einen Wert füllen der andere müsste unverändert bleiben.

    Hier liegt die Krux.


  • Administrator

    ChickenWing schrieb:

    Beim ConvertBack wird es schwieriger. Da erhalte ich den Wert der Textbox und muss ein Array zurückgeben. Sozusagen müsste ich nur einen Wert füllen der andere müsste unverändert bleiben.

    Hier liegt die Krux.

    Das geht, wenn du die Dokumentation zu IMultiValueConverter.ConvertBack liest:

    MSDN schrieb:

    Return DoNothing at position i to indicate that no value is to be set on the source binding at index i .

    Grüssli



  • Oh super. Danke so weit habe ich gar nicht geschaut.

    Da habe ich nun nur noch eine Frage.

    Wie triggere ich denn nun das der Wert dann beim Button klick in die Property im ViewModel geschrieben wird?


  • Administrator

    ChickenWing schrieb:

    Wie triggere ich denn nun das der Wert dann beim Button klick in die Property im ViewModel geschrieben wird?

    Musst du gar nicht, geschieht doch automatisch. Sobald der Focus in der TextBox verloren geht, wird der Wert der TextBox in das Property im ViewModel geschrieben.

    Alles was du somit brauchst, ist ein ICommand Objekt des ViewModels an den Button zu hängen.

    Grüssli



  • Nein wird nicht automatisch aufgerufen. Zumindest nicht wenn ich in der Textbox nicht eintippe. Wenn ich also in der Listview einen Wert auswähle wird der in der Textbox angezeigt.

    Rufe ich nun meinen Button auf wird mein Fenster geschlossen. Die Convertback Methode wird aber nicht ausgeführt.


  • Administrator

    Wenn du nichts in der TextBox eingibst, verändert sich die Text -Property auch nicht, wodurch es nie ein ConvertBack auslöst. Du wirst womöglich nun sagen: "Wenn ich ein Element in der ListView auswähle, dann verändert sich die Text -Property doch!"
    Ja, aber aus dem Resultat eines Binding des MultiBinding . Dabei wird nur Convert aufgerufen. Wenn dann auch noch ConvertBack aufgerufen würde, könnte dies in eine Endlosschleife enden. Daher wird dies nicht gemacht. Es wird eigentlich auch nicht erwartet, dass man MultiBinding so verwendet.

    Wie ich schon sagte, MultiBinding ist dafür eigentlich nicht gedacht.

    Grüssli



  • Ok dann halt doch die andere Lösung. Dank dir.



  • Habe nun deine Lösung getestet. Allerdings funktioniert die ja gar nicht. Der Wert in der Textbox wird nur eimalig aus der ListView angezeigt. Beim Wechseln der selektierung ändert sich nichts mehr.

    Zum anderen macht die set Methode gar nichts. Ausser ein Property Changed zu werfen.


  • Administrator

    ChickenWing schrieb:

    Habe nun deine Lösung getestet. Allerdings funktioniert die ja gar nicht. Der Wert in der Textbox wird nur eimalig aus der ListView angezeigt. Beim Wechseln der selektierung ändert sich nichts mehr.

    Bei mir hat alles funktioniert. Hast du das CurrentChanged Event korrekt abonniert? Rufst du darin das PropertyChanged Event korrekt auf? IsSynchronizedWithCurrentItem="True" gesetzt?

    ChickenWing schrieb:

    Zum anderen macht die set Methode gar nichts. Ausser ein Property Changed zu werfen.

    Ja, also ein wenig selber programmieren darfst du natürlich auch noch. Deshalb habe ich dort ja auch den Kommentar hinterlassen, welcher dir auch ein weitergehendes Vorgehen erklärt.

    Grüssli



  • Hallo Dravere.

    Oh sorry hatte da noch nen kleinen Fehler drin. Das mit der Auswahl funktioniert nun.

    Ja, also ein wenig selber programmieren darfst du natürlich auch noch.

    Klar. Hatte deinen Kommentar überlesen. Sorry nochmals.

    Aber nun habe ich noch das Problem, dass die SetMethode nur einmal bei start aufgerufen wird. Das ist doch das selbe Problem wie beim ConvertBack?

    Oder geht das bei dir?



  • Ok habe deine Lösung nun so abgeändert dass Sie funktioniert.

    OnCurrentRecipeChanged sollte nicht das RaisePropertyChanged Event werfen.
    sondern direkt das Property setzen

    private void OnCurrentRecipeChanged(object source, EventArgs e)
    {
        CurrentRecipeName = GetSelectedRecipe().Name;
    }
    
    private string _currentRecipeName;
    public string CurrentRecipeName 
    {
          get
          {
            return __currentRecipeName;
          }
          set
          {
            __currentRecipeName = value;
            RaisePropertyChanged("CurrentRecipeName");
          }
    }
    

    So funktionierts. 😉


  • Administrator

    Damit meinte ich, dass du auch noch etwas selber programmieren sollst. Logisch musst du das so machen. Ich hoffe, dass dir auch klar ist wieso.

    Grüssli



  • Logisch musst du das so machen. Ich hoffe, dass dir auch klar ist wieso.

    Ja, klar ist mir das nun auch. Nun verstehe ich aber nicht wieso du mir das mit dem RaisePropertyChanged in der Methode OnCurrentRecipeChanged gezeigt hast.


Anmelden zum Antworten