Editable ComboBox mit WPF
-
Hallo,
Ich möchte gerne folgendes Verhalten für eine WPF ComboBox realisieren.
Der selektierte Eintrag soll dann editierbar sein, und zwar so, dass via Data Binding im entsprechenden (selektierten) Item der Wert der gebundenen Eigenschaft geändert wird.Als Bsp. folgender Code.
Dabei soll z.B. Banana in Grapefruit editiert werden.Windows.xaml.cs
using System.Windows; using System.Collections.Generic; namespace Test { public class Fruit { public Fruit(string name) { Name = name; } public string Name { get; set; } } public partial class Window1 : Window { public Window1() { InitializeComponent(); List<Fruit> fruits = new List<Fruit>(); fruits.Add(new Fruit("Banana")); fruits.Add(new Fruit("Kiwi")); comboBox.ItemsSource = fruits; } } }
Windows.xaml
<Window x:Class="Test.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <StackPanel> <ComboBox Name="comboBox" DisplayMemberPath="Name" IsEditable="True" /> </StackPanel> </Window>
Hat jemand schonmal sowas gemacht? Ich konnte keine vernünftigen Ansätze im Netz finden.
Simon
-
so wie es da steht muesste es doch denk ich funktionieren
ich glaub der eintrag in der combo wird einfach nicht aktualisiert da du kein propertychanged sendest
-
Leider funktioniert es so nicht.
Das INotifyPropertyChanched interface habe ich testeshalber auch mal eingebaut.Ich habe auch versucht, ein Binding zu definieren:
Text="{Binding Path=Name,UpdateSourceTrigger=PropertyChanged}"
Da hilft jedoch auch nicht.
Simon
-
ist die varaible nach dem aendern auch im code geaendert ?
-
Mr Evil schrieb:
ist die varaible nach dem aendern auch im code geaendert ?
Ja.
Das Problem hierbei ist, dass wenn ein anderer Eintrag selektiert wird, der alte mit dem neuen überschrieben wird.Simon
-
du solltest eventuell dein binding ueberarbeiten
probiere mal folgendes:
FruitsDisplayView.xaml
<ComboBox ItemsSource="{Binding Fruits}" IsSynchronizedWithCurrentitem="True" SelectedItem="{Binding Fruit}" IsEditable="True" />
FruitsDisplayViewModel .cs
public class FruitsDisplayViewModel : ViewModelBase { public FruitsDisplayViewModel(List<Fruits> fruits) { Fruits = new ObservableCollection<FruitViewModel>(); foreach (Fruit fruit in fruits) Fruits.Add(new FruitViewModel(fruit)); Fruit = Fruits.FirstOrDefault(); } public ObservableCollection<FruitViewModel> Fruits { get; set; } public FruitViewModel Fruit { get { return _fruit; } set { _fruit = value; OnPropertyChanged(this, "Fruit"); } } private FruitViewModel _fruit; }
FruitViewModel.cs
public class FruitViewModel : ViewModelBase { public FruitViewModel(Fruit fruit) { Model = fruit; } public Fruit Model { get; set; } public string Name { get { return Model.Name; } set { Model.Name = value; OnPropertyChanged(this, "Name"); } } }
Fruit.cs
public class Fruit { public string Name { get; set; } }
so - das ist nun schonmal die grundlage
nun - jedesmal wenn du ein neues item selektierst wird "Fruit" im FruitsDisplayViewModel aufgerufen und an dem item gearbeitet
sobald du ein neues item auswaehlst aendert sich das selecteditem aber das alte muesste den wert weiterhin behalten
setz dir dann einfach ein breakpoint in den Fruit setter im FruitsDisplayViewModel und beobachte die werte in der Fruits liste ob bei denen was passiert(im selben zusammenhang kannst du dir gleich mvvm an schauen, da hast du immer eine liste mit fruit objekten mit denen du arbeiten kannst und du hast auch immer das aktuell selektierte item - du kannst dann auch problemlos die combobox mit einer listbox oder listview austauschen ohne den c# code aendern zu muessen - halt vorteile des mvvm)
-
@ mr evil: Ist das MVVM(heißt es so?)?.Außerdem, würde es nicht einfach reichen wenn man die Mode(Bindungsrichtung) im XAML mit angibt,oder ist die Standardmäßig auf TwoWay eingestellt?
Also in etwa so:<ComboBox ItemsSource="{Binding Fruits}" IsSynchronizedWithCurrentitem="True" SelectedItem="{Binding Fruit}" IsEditable="True" Mode=TwoWay />
Oder bewirkt das dann nichts und man muss das MVVM implementieren, weil der Overhead der dadurch entsteht nur um so ein kleines Feature zu implementieren scheint mir recht hoch oder ist das normal? Du hast die Erfahrung klär mich auf.
-
japs - das ist mvvm siehst du absolut richtig (habs nur hier im forum schnell runter getickert -> keine garantie auf funktionalitaet #gg)
soweit ich weiss ist bei textboxen der default two way - drum geh ich davon aus das es bei der combobox auch so sein wird (wird vermutlich auch nur ne textbox verwenden)
ich denk nicht das das binden das problem ist - es wurde ja gesagt das es im code dann richtig sitzt
irgendwo laeuft was schiefdiesen ansatz muss man natuerlich nicht verfolgen - ich habe halt immer den dran es wenn dann gleich absolut richtig gzu machen #gg
man kann auch so das SelectedItem binden
mir ging es hauptsaechlich darum das man durch den weg der objekte genau verfolgen kannalso
binde das selecteditem entsprechend (issynchonizedwithcurrentitem muss true sein) und setz ein breakpoint an dem setter
wenn du dann neuen text eingibst und dann die auswahl aenderst kann man mal nach schauen ob sich der wert geaendert hat - wenn nein hat man da ein ansatz - wenn ja kann man schauen ob der wert in der liste sich ebenso geaendert hat (muesste eigentlich, da es das selbe objekt ist)
und zu guter letzt kann man dann im get checken ob der neue wert danach weiterhin in der liste ist
wenn ja gibts nur ein anzeige problem - wenn nein hat man auch wieder ein ansatzdreh und angelpunkt ist schlichtweg das get und set vom "Fruit" propertie welches SelectedItem repraesentiert
kann bei gelegenheit ja mal schauen ob ich es nachstellen kann - heute werd ich nicht dazu kommen denk ich - morgen frueh vieleicht
btw
<ComboBox ItemsSource="{Binding Fruits}" IsSynchronizedWithCurrentitem="True" SelectedItem="{Binding Fruit}" IsEditable="True" Mode=TwoWay />
das ist kaese
Mode ist ein property vom Binding objekt - man kann hoechstens SelectedItem="{Binding Fruit, Mode=TwoWay}" machen - aber nicht so wie du es darstellst (auch sehr auffaellig da TwoWay nicht in "
haste evtl nur falsch gepastetmore btw
overhead fuer ein feature ist es in diesem fall schon - nur wenn man eine applikation komplett auf mvvm hat sieht alles so aus, da ist es nur eines von vielen
habe auf arbeit auch schon mittelgrosse projekte welche komplett in mvvm geschrieben sind, und daheim trenn ich sogar die exe, views, viewmodels und models in separate dll's auf {= (kommt auf arbeit noch - fehlt bisher nur die freigabe #gg)
die mvvm regeln selber denk ich brauch ich ja nicht weiter zu erlaeutern - machs aber trotzdem mal:
- alle code behinds leer (die unart einer "code behind" wird dadurch obsolet)
- die models sind businesslogic und daten und kennen keinerlei ausgabe ausser eventuell log files
- die views sind nur eine schablone zur darstellung der daten und weiterleiten von commands (klicks usw)
- die viewmodels sind nur der proxy um die daten aus den models darstellbar zu machen und um commands an die models weiter zu leiten
- die viewmodels erstellen keine direkte view elemente (windows, controls, dialoge, messageboxen)
- views, viewmodels und models koennen jederzeit in unterschiedlichen umgebungen eingesetzt werden und sind auch allein "lauffaehig" (stichwort: unittests)
ueber mvvm koennte man noch stunden quatschen - sowas macht man dann eher per mail oder in ein separaten thread
-
Jo hattest Recht, habs falsch geschrieben, naklar muss das mit in das BindingObjekt, mein Fehler.
Sehr interessantes Thema das MVVM, aber das hatten wir ja schonmal mit Dravere auseiander gefrickelt.Ich sehe es ganz genauso wie du, man sollte versuchen die Logik und alles andere so gut von dem Rest zu trennen das man es ohne weiteres auch woanders nutzen kann, so bleibt man relativ abstrakt.
Danke für die Ausführung.
-
so
ich musste das problem der editable combobox nun auch loesen
das problem an der ganzen sache war das "SelectedItem" auf null geht sobald man text eingibt
um das zu loesen kann man Text binden
da war dann wieder das problem das SelectedItem null ist wenn Text neuer text war
man koennte nun entweder - wenn neuer text eingegeben wurde und die combobox verliert den focus den eintrag neu hinzu fuegen - am sonsten kann man sich einfach die letzte selektion merken und die dann neu setzenhier meine funktionierende implementation:
public class Fruit { public string Name { get; set; } }
public class FruitViewModel : ViewModelBase { public FruitViewModel(Fruit fruit) { Fruit = fruit; } public string Name { get { return Fruit.Name; } set { Fruit.Name = value; OnPropertyChanged(this, "Name"); } } public Fruit Fruit { get { return _fruit; } set { _fruit= value; OnPropertyChanged(this, "Fruit"); } } private Fruit _fruit; public override string ToString() { return Name; } }
public class MainViewModel : ViewModelBase { public ObservableCollection<FruitViewModel> Fruits { get; set; } public MainViewModel() { Fruits= new ObservableCollection<FruitViewModel>(); Fruits.Add(new FruitViewModel(new Fruit() { Name = "Banane" })); Fruits.Add(new FruitViewModel(new Fruit() { Name = "Ananas" })); Fruits.Add(new FruitViewModel(new Fruit() { Name = "Apfel" })); SelectedUser = Fruits.FirstOrDefault(); } public FruitViewModel SelectedFruit { get { return _selectedFruit; } set { _selectedFruit = value; if (_selectedFruit != null) _oldSelectedFruit = _selectedFruit; OnPropertyChanged(this, "SelectedFruit"); } } private FruitViewModel _selectedFruit; private FruitViewModel _oldSelectedFruit; public string NewFruitName { get { return _newFruitName; } set { _newFruitName= value; if (SelectedFruit == null) _oldSelectedFruit.Name = _newFruitName; OnPropertyChanged(this, "NewFruitName"); } } private string _newFruitName; }
<!-- Wenn man das ItemTemplate nicht setzt wird wird die drop down liste nicht aktualisiert da es fuer "ToString()" kein PropertyChanged geben kann --> <ComboBox ItemsSource="{Binding Fruits}" IsSynchronizedWithCurrentItem="True" IsEditable="True" Text="{Binding NewFruitName}" SelectedItem="{Binding SelectedFruit}"> <ComboBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Name}" /> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox>