Laden...

[erledigt] DataSource einer ComboBox ändern?

Erstellt von Uwe81 vor 12 Jahren Letzter Beitrag vor 12 Jahren 2.185 Views
U
Uwe81 Themenstarter:in
282 Beiträge seit 2008
vor 12 Jahren
[erledigt] DataSource einer ComboBox ändern?

Hallo!

Folgendes Problem (ViewModel):


    class Foo {
        public string Name { get; set; }
    }

    class Bar {
        public string Name { get; set; }
    }

    public enum RefType {
        FooType,
        BarType
    };

    class ViewModel : INotifyPropertyChanged {
        RefType _refType;

        public ObservableCollection<Foo> Foos { get; private set; }
        public ObservableCollection<Bar> Bars { get; private set; }

        public RefType RefType {
            get { return _refType; }
            set {
                if (RefType != value) {
                    _refType = value;
                    RaisePropertyChagend("RefType");
                }
            }
        }

        //.......
    }

Jetzt binde ich eine ComboBox an die Foos, eine ComboBox an die Bars und eine an den RefType. Klappt alles.

Nun möchte ich noch eine weitere ComboBox, in der folgendes steht:
Wenn RefTypeFooType, soll die ComboBox die Namen aller Foos enthalten. Wenn RefTypeBarType, soll die ComboBox die Namen aller Bars enthalten.

Allerdings können sich die Collections Foos und Bars zur Laufzeit ändern. Wären diese statisch, würde ich im setter von RefType eine Collection setzen, und die ComboBox an diese binden:


        public RefType RefType {
            get { return _refType; }
            set {
                if (RefType != value) {
                    _refType = value;
                    RaisePropertyChagend("RefType");
                    switch (_refType) {
                        case RefType.BarType:
                            Names = Bars.Select(x => x.Name).ToArray(); break;
                        case RefType.FooType:
                            Names = Foos.Select(x => x.Name).ToArray();
                            break;    
                    }
                }
            }
        }

        IEnumerable<string> _names;
        public IEnumerable<string> Names {
            get { return _names; }
            private set {
                if (Names != value) {
                    _names = value;
                    RaisePropertyChagend("Names");
                }
            }
        }

Dann bekomme ich aber nicht mit, wenn ein neuer Foo bzw. Bar hinzugefügt wird.
Ich habe schon Optics entdeckt, was vermutlich das Problem lösen könnte (noch nicht probiert).

Gibt es eine im .NET-Framework enthaltene Lösung dafür?

Vielen Dank,
Uwe

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo Uwe81,

wenn Foo und Bar so gleich sind kannst du eine Basisklasse davon erstellen und statt dem Switch Polymorphie anwenden.

Im Setter setzt du dann zuerst den Eigenschaftswert und dann feuerst du das NotifyPropertyChanged-Ereignis. Statt IEnumerable<T> sollte beim Binden auch besser IList<T> od. die ObservableCollection<T> verwendet werden. Für die IEnumerable<T> erstellt die WPF sonst auch nur einen IList<T>-Wrapper und das ist unnötiger Overhead.

Ich merke gerade, dass ich die einzige konkrete Frage gar nicht beantwortet habe:

Gibt es eine im .NET-Framework enthaltene Lösung dafür?

Ja. (Und somit hätte ich mir den oberen Teil der Antwort sparen können, mehr war ja nicht gefragt 😉

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

U
Uwe81 Themenstarter:in
282 Beiträge seit 2008
vor 12 Jahren

Also erstmal: Mit Optics geht es....


        public RefType RefType {
            get { return _refType; }
            set {
                if (RefType != value) {
                    _refType = value;
                    RaisePropertyChagend("RefType");
                    switch (_refType) {
                        case RefType.BarType:
                            Names = ExpressionObserver.Execute(this, t => t.Bars.Select(x => x.Name)).Cascade();
                            break;
                        case RefType.FooType:
                            Names = ExpressionObserver.Execute(this, t => t.Foos.Select(x => x.Name)).Cascade();
                            break;    
                    }
                }
            }
        }

Die Lösung mit der Polymorphie ist mir noch nicht klar...


    class Base {
        public string Name { get; set; }
         
    }

    class Foo : Base { }

    class Bar : Base { }

Aber was ist nun das Property Names? Ein ObservableCollection<Base>? Da kann ich dann zwar einzeln Foos oder Bars rein tun, aber doch niemals die ganze Liste zuweisen (ist leider nicht Kovariant). Wenn ich aber nur einzelne Werte hinzufüge, muss ich wieder händig auf das Hinzufügen neuer der Foos und Bars reagieren.

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo Uwe81,

Ein ObservableCollection<Base>?

Ja und wenn eine Base od. davon abgeleitetes Objekt hinzugefügt wird gehts auch. Beim Bindung kannst du dann als Path die Name-Eigenschaft angegeben und je nach konkretem Typ wird dann (eben wegen der Polymorphie) vom richtigen Objekt der Wert genommen.

Da kann ich dann zwar einzeln Foos oder Bars rein tun, aber doch niemals die ganze Liste zuweisen (ist leider nicht Kovariant).

Das "Problem" hast du ja immer: bei einer List<Foo> musst du auch einzelen Foos zuweisen. Das ist hier nicht anders.

Wenn ich aber nur einzelne Werte hinzufüge, muss ich wieder händig auf das Hinzufügen neuer der Foos und Bars reagieren.

Nein musst du nicht. Das übernimmt die ObserableCollection<Base>.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

U
Uwe81 Themenstarter:in
282 Beiträge seit 2008
vor 12 Jahren

Ok, wenn ich dich richtig verstehe, verwende ich statt

 
public ObservableCollection<Foo> Foos { get; private set; }
public ObservableCollection<Bar> Bars { get; private set; }

dann


public ObservableCollection<Base> Foos { get; private set; }
public ObservableCollection<Base> Bars { get; private set; }

und kann diese dann dem Property Names zuweisen.

Eigentlich ziemlich offensichtliche Lösung (muss wohl auf dem Schlauch gestanden haben), allerdings zu dem Preis, dass ich in dem ViewModel typsicherheit verliere.

U
Uwe81 Themenstarter:in
282 Beiträge seit 2008
vor 12 Jahren

Habe jetzt die folgende Lösung:


class Base {
     public string Name { get; set; }
}

class Foo : Base { }
class Bar : Base { }

public ObservableCollection<Foo> Foos { get; private set; }
public ObservableCollection<Bar> Bars { get; private set; }
public IEnumerable<Base> References { get; private set; } //Wirft PropertyChangedEvent

Dann kann ich die Foos und Bars den References zuweisen (weil IEnumerable Kovariant ist), beim Binding wird das aber Anscheinend über Reflection aufgelöst, so dass auch bei References auf CollectionChanged-Events reagiert wird.

Dennoch bleibt die Typsicherheit bei Foo und Bar erhalten.

Selbst wenn Foo und Bar nicht von Base ableiten, könnte man References vom Type IEnumerable (nicht generisch) oder object definieren.