Laden...
S
sth_Weird myCSharp.de - Member
Dipl.Ing IT (BA) BaWü Dabei seit 17.01.2007 469 Beiträge
Benutzerbeschreibung

Forenbeiträge von sth_Weird Ingesamt 469 Beiträge

31.05.2017 - 08:52 Uhr

Ja ich hatte auch schon befürchtet, dass ich um Reflection nicht rumkomme.

Gruß & Danke
sth_Weird

30.05.2017 - 12:20 Uhr

Hallo,
ich habe ein Problem beim Zugriff auf eine generische Klasse, die ein Interface implementiert...
Ich bekomme von einer externen Quelle (auf die ich keinen Einfluss habe) ein Objekt vom Typ IMessage übergeben. Dieses enthält im konkreten Fall eine generische Implementierung von IMessage -> GenericMessage<T>. GenericMessage<T> enthält ein Property DataItem vom Typ T. Der Typ von T interessiert mich an dieser Stelle nicht, ich möchte einfach das, was in DataItem steht, in einer lokalen Variablen (vom Typ object) speichern und später weiterverarbeiten (sagen wir der Einfachheit halber mal, ich möchte das Objekt mit ToString in der Console ausgeben (angenommen ToString von T liefere informative Daten).
Aber wie komme ich an diese Information ran? Ich weiß, dass das Objekt, das mir als IMessage übergeben wird, vom Typ GenericMessage<T> ist, kenne aber den Typ von T nicht und möchte auch nicht alle möglichen bekannten Typen von T durchprobieren, ich möchte einfach nur an das DataItem Property kommen und das in einer lokalen Variablen vom Typ object speichern...

Gruß & Danke
sth_Weird

17.03.2017 - 09:51 Uhr

Hmm, dann komme ich wohl nicht um das compilen drum rum...
Danke für die Antwort!

gruß
sth_Weird

14.03.2017 - 10:38 Uhr

hallo,
Ich würde gerne eine Expression<Func<T,Res>> in String-Form ausgeben.
Dafür habe ich einen ExpressionVisitor implememtiert (von der abstrakten Basisklasse abgeleitet).
Soweit so gut, nur die Parameter werden nicht so angezeigt, wie ich das gerne hätte.
Eine einfache Expression sieht z.B. so aus:


p => p.Age >= myLocalIntVariable

myLocalIntVariable ist eine lokale int-Variable in einer Funktion (der Wert wird aus der GUI ausgelesen).
Im ExpressionTree ist myLocalIntVariable eine ConstantExpression. Ich möchte hier den Wert ausgeben, der dort gespeichert ist. Leider steht im .Value Property der ConstantExpression was "kryptisches" drin, und nicht der einfache Zahlenwert, den ich haben möchte. Ich habe nun soviel über ConstantExpressions gelesen, dass ich weiß, dass da ein Zwischenspeicher für den Wert erzeugt wird, in dem Value als object gespeichert wird. Wenn ich beim Debuggen die ConstantExpression im QuickWatch angucke, kann ich das Value Property aufklappen und sehe dann den tatsächlichen Zahlenwert, der drin steht. Aber über den Code scheine ich da so nicht ran zu kommen.
Ich habe Beispielcode gefunden, wie ich einen ExpressionTree selbst aufbaue, und so mit bestimmten Tricks erreichen kann, dass meine lokale Variable wirklich als Wert in den ExpressionTree reinwandert (also so als hätte ich geschrieben p => p.Age ≥ 5 oder so). Das ist natürlich wesentlich komplizierter als die Syntax oben. Daher frage ich mich, ob ich man nicht mit einfacheren Mitteln zum gleichen Ziel kommt... Ich habe nämlich zum Teil verschachtelte Prüfungen, wie p => p.x.y.z > a.b.c, und denke den Tree da händisch aufzubauen wird doch ganz schön komplex...
Auch über alternative, einfache Wege zum Ziel wäre ich dankbar.
(ich habe schonmal gelesen dass man durch compilen der Expression weiter kommen könnte, aber das sei sehr langsam, und da ich die Expression auf viele Datensätze loslassen will 😕 ...)

gruß & danke
sth_Weird

21.02.2017 - 09:05 Uhr

List<LambdaExpression> funktioniert, danke!

20.02.2017 - 12:58 Uhr

Hi,
ich habe mir eine GUI zum Anlegen von Filtern geschrieben. Jeder Filter ist definiert als Expression<Func<T, bool>>.
Nun möchte ich alle Filter in einer Liste speichern. Der Haken dabei: das T ist nicht bei allen Filtern der gleiche Datentyp, es kann also einmal Expression<Func<string, bool>> sein, ein anderes Mal Expression<Func<MyDataType, bool>>. Zusatzinfo: Nicht alle Filter sind für ein und dasselbe Ziel gedacht, daher sind die unterschiedlichen Datentypen kein Widerspruch.
Nun dachte ich mir, das einfachste ist, ich verwende als Datentyp object, da das ja die Oberklasse von allem ist.

List<Expression<Func<object, bool>>> myList;
...

Wenn ich nun aber Daten einfügen will:


Expression<Func<MyDataType, bool>> myExpression = ...;
// das geht nicht
myList.Add(myExpression);
// das geht auch nicht:
myList.Add((Expression<Func<object, bool>>)myExpression)

dann meckert der Compiler, er könne Expression<Func<MyDataType,bool>> nicht umwandeln in Expression<Func<object, bool>>.

A) Warum kommt hier diese Fehlermeldung, wo object doch die "Überklasse" ist. In eine List<object> kann ich ja auch alles adden.

B) Wie muss ich die Liste definieren bzw. wie meine Expression einfügen, damit es geht, oder ist die einzige Alternative (wenn ich mir keine eigene Listenklasse anlegen will...), die ganze Expression-Liste als List<object> anzulegen?

gruß & danke
sth_Weird

20.12.2016 - 10:52 Uhr

Das hatte ich garnicht mehr ausprobiert, da ich gleich die Meldung gesehen hatte. Habe es nochmal zurückgebaut und ja, die TextBox war trotzdem gesperrt...

Ich hätte erwartet, dass hier optimiert wird...also erst mal "im Background" alles ausgewertet wird, und dann für das jeweilige Property nur das angewandt wird, "was am Ende da steht". Aber deine Erklärung mit dem roten/blauen Auto leuchtet schon ein...

Gruß
sth_Weird

19.12.2016 - 11:31 Uhr

Ich habe noch etwas rumprobiert und folgendes herausgefunden:

  • ich habe ein benanntes Style erstellt für meine Textbox, die ReadOnly sein soll. Das einzige, was dieses Style macht ist, dass es ReadOnly immer auf True setzt.
  • wenn ich der besagten TextBox im ItemTemplate dieses Style zuweise, dann funktioniert es, die Meldungen mit dem fehlenden Property sind weg und die TextBox ist immer ReadOnly, wie gewünscht 😃
    Weshalb es nicht funktioniert, wenn man nur das ReadOnly überschreibt, wüsste ich trotzdem gerne noch...

gruß
sth_Weird

19.12.2016 - 10:38 Uhr

Hallo,
ich habe ein Problem mit einem View.
Ich habe in den Resources ein Style für TextBoxen angelegt. Dieses Style setzt, abhängig von einem Wert im ViewModel des Views, alle TextBoxen auf ReadOnly oder Schreibbar. Zur Veranschaulichung nehmen wir mal an das ViewModel hieße "MainViewModel" und das Property "WriteProtection"
Der View enthält auch ein ItemsControl. Das ItemsControl zeigt eine Liste mit ViewModels "ElementViewModel" an (die Liste der ElementViewModels ist im MainViewModel als Property enthalten).
Das ItemTemplate enthält nun wiederum auch eine TextBox. Diese TextBox will ich immer ReadOnly haben. Daher habe ich explizit für diese TextBox im XAML das Property IsReadOnly=True definiert.
Nun meckert VS für die TextBox im ItemsControl immer, "ElementViewModel" hätte kein "WriteProtection" Propery, welches vom Property "IsReadOnly" der TextBox verlangt würde.
(nur zur Klarstellung: Dass mein angezeigtes Objekt im ItemsControl einen anderen DataContext (= aktuell angezeigtes ElementViewModel) hat und damit das WriteProtection des MainViewModels nicht kennt ist mir klar, also da liegt nicht das Verständnisproblem...)
Ich verstehe die Fehlermeldung nicht, da ich bei IsReadOnly explizit True angegeben habe. Dieser Wert, der explizit für das Control angegeben ist, müsste doch eigentlich den Wert aus dem Style, der in den Resourcen angegeben ist, überschreiben, oder???
Mein Kenntnisstand war, dass explizit angegebene Werte höherprior sind als die Werte die in den Resourcen des eigenen View/UserControl stehen, und diese wiederum höherprior als die Resourcen, die in anderen, verwiesenen Resourcen stehen.

Wo ist mein Denkfehler, warum wird fürs ItemsControl-Template scheinbar das "IsReadOnly" aus dem Style verwendet und nicht mein explizit angegebener Wert?

In den Resources des Views (relevanter Ausschnitt):


<Style TargetType="TextBox">
                <Setter Property="IsReadOnly" Value="{Binding Path=WriteProtection, ... /Setter>

ItemsControl im View (nur die relevanten Ausschnitte):


 <ItemsControl x:Name="ICtrlElements"... ItemsSource="{Binding Path=MyElements}" >
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid Margin="0,0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="120" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <TextBox Grid.Column="1" Text="{Binding ExampleProperty, Mode=OneTime}" IsReadOnly="True" TextWrapping="Wrap" />

Der Fehler:> Fehlermeldung:

System.Windows.Data Error: 40 : BindingExpression path error: 'WriteProtection' property not found on 'object' ''ElementViewModel' (HashCode=49829907)'. BindingExpression:Path=WriteProtection; DataItem='ElementViewModel' (HashCode=49829907); target element is 'TextBox' (Name=''); target property is 'IsReadOnly' (type 'Boolean')

gruß & danke
sth_Weird

21.10.2016 - 11:24 Uhr

Besagtes NumericTextBoxBehaviour halte ich für einen furchtbaren Anwendungsfall für AttachedBehaviours, denn WPF stellt genau für diesen Zweck eben schon Eingabevalidierungen zur Verfügung. Aber das ist meine persönliche Meinung.

Also dieses NumericTextBoxBehaviour verwende ich, damit ich direkt Int-Properties im View anbinden kann. Gebe ich sonst einen String in eine TextBox ein, die mit einem Int-Property verbunden ist, dann fliegt ne Exception.
Als Work-Around im Netz steht, man könnte an ein String-Property binden und nur wenn die Eingabe gültig ist, den Wert auf das tatsächliche Int-Property schreiben. Da empfinde ich dann aber ein Behaviour (oder auch eine ValdiationRule) als elegantere Lösung...
Jetzt nach deinem Kommentar frage ich mich, von welchen (anderen?) Eingabevalidierungen du sprichst, die WPF schon zur Verfügung stellt (und ich evtl noch nicht kenne)???
Mir fiele zum Thema Eingabevalidierung nur noch das IDataError-Interface ein, welches ich auch bei mir implementiert habe für weiterführende Validierungen. Aber dieses kommt erst später zum Zug und fängt falsche Eingaben in meine (an ein Int-Property gebundene) TextBox nicht ab...

Aber danke schon mal für deine Rückmeldung zum Thema Testen. So hatte ich mir das auch gedacht - also dass man jede Komponente einzeln testet, ob sie das macht was sie soll, und sich dann darauf verlässt, dass sie im Einsatz in einem größeren System das richtige macht. Sonst wären größere Systeme wahrscheinlich auch garnicht testbar...

gruß
sth_Weird

19.10.2016 - 12:55 Uhr

Das mit dem Int ist aber ein sehr einfacher Fall...
Um mal etwas komplexeres zu nehmen, ich könnte ein AttachedBehaviour haben, das nur IP-Adressen oder GUIDs als Eingabe zulässt (angenommen das ViewModel würde diese als String anbieten).
Es ist eine komfortable Vorstellung, dass ich nur meinen Textboxen in den Views das AttachedBehaviour zuweisen muss und es werden nur gültige Eingaben erlaubt. In den ViewModels muss ich nichts weiter machen (es sei denn ich muss noch Dinge prüfen, die für dieses ViewModel spezifisch sind).

Es gibt neben AttachedBehaviours z.B. auch ValidationRules, welche man direkt im XAML verwenden kann und welche Eingaben prüfen können...das Problem müsste hier dasselbe sein - ich bekomme die Fehler nicht in mein ViewModel! Mir scheint manchmal, dass sich diese Mechanismen und das MVVM-Prinzip hier in die Quere kommen 😕

Gruß
sth_Weird

19.10.2016 - 11:15 Uhr

Hallo,

ich habe eine Anwendung mit MVVM-Pattern. Viele Eingaben in meinem View erfordern ein bestimmtes Format, z.B. nur Zahlenwerte, Texte die nur eine bstimmte Länge haben dürfen, Texte die nur in Großbuchstaben sein dürfen, Texte ohne Leer/Sonderzeichen etc.
Ich liebäugle hier mit einem AttachedBehaviour zur Eingabeprüfung, im Internet habe ich auch schon Beispiele gefunden (z.B. NumericTextBoxBehaviour), die ich gut auf meine Anforderungen anpassen könnte.
Ich frage mich nun aber, wie man so etwas testet. Teste ich nur das ViewModel, würde dieses ja Werte annehmen, die nicht gültig sind, da in der realen Umgebung der View die Werte durch das AttachedBehaviour abweisen würde. Andererseits, wenn ich die Prüfung im ViewModel nochmal implementiere, wozu dann überhaupt noch das AttachedBehaviour (die Prüfungen wären ja doppelt implementiert und das ganze damit sogar noch fehleranfällig).
Oder testet man in dem Fall das AttachedBehaviour einzeln und wenn das passt, dann testet man das ViewModel nur noch mit "guten" Eingaben, da man weiß, dass gar keine schlechten durchkommen?
Oder testet man beide in Kombination, d.h. man arbeitet im UnitTest mit ViewModel UND AttachedBehaviour und schickt einen neuen Wert erst an eine (evtl. statische) Prüffunktion im AttachedBehaviour, bevor man ihn an das ViewModel übergibt?

Gruß
sth_Weird

24.02.2016 - 11:28 Uhr

Einfacher als mit TemplateSelector gehts wenn man (Default-)DataTemplates für die ViewModels erstellt. Dann wird die Ansicht in der ListView automatisch je nach VM-Type angepasst (auch wenn neue dazukommen muss man nichts weiter machen als ein neues DataTemplate dafür anzulegen).
Mit dem TemplateSelector kann man komplexere Sachen realisieren, da ist man maximal flexiblel, in den meisten (einfachen) Fällen braucht man ihn aber nicht.

gruß
sth_Weird

19.11.2015 - 12:43 Uhr

Ich würde auf keinen Fall den VM Namen in der Datenbank speichern, sondern dafür eine Enumeration erstellen. Damit kannst du sicherstellen, dass nur erlaubte Werte eingetragen werden und bist auch vor Namensänderungen sicher! In der Datenbank steht nur die "ID" bzw. der Enumerationswert, diesen kannst du im C# Code auswerten und das entsprechende ViewModel erstellen.
Die ganze if/else Geschichte kannst du noch umgehen, wenn du dir im Code ein Dictionary hältst, welches die Enumerationswerte mit dem Ziel-ViewModel verknüpft (key = Enumeration, value = ViewModel-Type). Dann kannst du für jeden Eintrag der Datenbank nachschlagen welches ViewModel gebraucht wird und dieses ViewModel dynamisch instanziieren. Kommen neue Enumerationswerte (= ViewModels) dazu, musst du nur noch das Dictionary erweitern, die Verwendungsstelle bleibt gleich.
Ansonsten aber sehe ich an deiner Herangehensweise nichts falsches. Es gibt sicher auch andere Wege, aber da müsste man das Gesamtsystem kennen..

gruß
sth_Weird

03.11.2015 - 10:01 Uhr

Hallo,

also bei mir fliegt eine Exception, dass der String nicht als ImageSource verwendet werden kann...diese Exception fliegt auch nur wenn mein Binding null liefert.

Ich habe es jetzt mit einem leeren Image gelöst und damit ich dessen Pfad nicht sehe, einen Trigger an den Pfad-TextBlock im DataTemplate gehängt, der diesen ausblendet wenn der Pfad des leeren Images drinsteht.

gruß
sth_Weird

02.11.2015 - 09:05 Uhr

Hallo,

ich habe in meinem View eine ComboBox zur Auswahl eines Bildes. Mein ViewModel liefert eine Liste mit Bildpfaden. Außerdem habe ich noch ein SelectedImage String im ViewModel. Ein DataTemplate erledigt, dass in der ComboBox die Daten als Image und Bildpfad darstellt werden. Soweit so gut.
Außer einem Bild soll es auch noch möglich sein, nichts (also kein Bild) auszuwählen. Hieran scheitere ich bisher. Ich habe meine String-Liste mit den Bildpfaden um einen Eintrag "null" erweitert. Nun wirft die Anwendung, wenn ein Objekt auf kein Bild verweist, natürlich eine Exception, dass das kein gülteriger Bildpfad ist, der als ImageSource verwendet werden kann.
Mein nächster Gedanke/Ansatz war, zwei unterschiedliche DataTemplates zu halten, eines für den Fall dass ein Bildpfad gegeben ist, eins für den "null"-Fall.
Nun kann ich nach dem Starten der Anwendung Objekte anzeigen, die kein Image haben, das funktioniert (es wird das richtige Template ohne Image ausgewählt). Kommt dann ein Objekt mit Image, wird das korrekte DataTemplate mit Image verwendet. Aaaber kommt als nächstes wieder ein Objekt ohne Image, fliegt wieder die altbekannte Exception, dass die ImageSource umgültig sei. Obwohl doch das andere Template ohne Image verwendet werden sollte!?
D.h. irgendwie versucht er wohl noch mit dem alten (Image-)Template zu binden, bevor das korrekte DataTemplate zum Zug kommt... Hier meine ComboBox-XAML:

<ComboBox Grid.Row="7" Grid.Column="1" Name="CboIconPath" ItemsSource="{Binding Path=Icons}" SelectedItem="{Binding Path=SelectedIcon, Mode=TwoWay, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Margin="10,2,5,2" HorizontalAlignment="Left" VerticalAlignment="Bottom" Width="400" >
                <ComboBox.Resources>
                    <DataTemplate x:Key="IconNameAndPic">
                        <StackPanel Orientation="Horizontal">
                            <Image Source="{Binding}" MinWidth="100" MinHeight="20"/>
                            <TextBlock Text="{Binding}" VerticalAlignment="Center" />
                        </StackPanel>
                    </DataTemplate>
                    <DataTemplate x:Key="IconNameOnly">
                        <TextBlock Text="{Binding}" VerticalAlignment="Center" />
                    </DataTemplate>
                </ComboBox.Resources>
                <ComboBox.Style>
                    <Style TargetType="ComboBox">
                        <Style.Setters>
                            <Setter Property="ItemTemplate" Value="{StaticResource IconNameAndPic}"></Setter>
                        </Style.Setters>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding}" Value="{x:Null}">
                                <Setter Property="ItemTemplate" Value="{StaticResource IconNameOnly}"></Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                    </ComboBox.Style>
                </ComboBox>

Hat jemand eine Idee wo das Problem liegt? Vielleicht daran, dass mein "Default-Template" (im Setter, ohne Trigger) das Template mit Bild ist und das Template ohne Bild im Trigger gesetzt wird (leider gibt es kein "Nicht Null" sonst könnte ich das ausprobieren)??? Wäre es ein Unterschied, anstelle im XAML mit Triggern zu arbeiten, einen TemplateSelector zu verwenden?
Ich hätte noch eine alternative Idee, statt "null" eine Art "EmtptyImage" Image zu erstellen, dann hätte man immer ein Bild...wenn es irgendwie mit "null" funktionieren würde wäre das aber natürlich weniger Aufwand...

gruß & danke
sth_Weird

30.09.2015 - 09:47 Uhr

Eine Instanz eines Controls kann es immer nur an einer Stelle geben. Es hat nur einen Parent. Fügt man es an einer anderen Stelle hinzu, dann ändert sich der Parent und es ist an der alten Stelle logischerweise nicht mehr da.
Mit der Shared=false Eigenschaft erzwingst du, dass an jeder Stelle quasi eine eigene Instanz des Controls erzeugt wird. Deshalb funktioniert es dann mit mehreren Verwendungsstellen.
Die Lösung von ErfinderDesRades ist trotzdem sauberer, vorausgesetzt du hast Zeit und Muse um das umzusetzen.
Für einfache, private Anwendungen geht die Lösung mit dem Shared aber auch, man sollte sich halt im Kopf behalten, dass es eher Quick & dirty Praktik ist...

gruß
sth_Weird

30.09.2015 - 09:35 Uhr

Ich krame das Thema noch mal raus da es dazu passt.
Mit dem oben erwähnten StringFormat klappt das alles bei mir so lange gut, bis der Anwender den kompletten Text der Textbox löscht oder eine inkompatible Eingabe macht. Dann fliegt eine FormatException, die Eingabezeichenfolge habe das falsche Format. Die Exception fliegt (logischerweise), bevor der Setter des Bindings aufgerufen wurde, ein Breakpoint im Setter des gebundenen Properties wird nicht angesprungen (wenn die Exception fliegt, steht die Anwendung in einer while(continueLoop)-Schleife in der Microsoft-Application.cs).
Gibt es eine Möglichkeit, diese Exception zu behandlen, oder muss man an dieser Stelle auf die Formatierung im XAML verzichten und es im Code lösen (z.B. mit einem Konverter).

gruß & danke
sth_Weird

28.07.2015 - 12:32 Uhr

da ich nicht weiß, ob ich deine Interpretation richtig verstanden habe, hier vielleicht noch mal anders:
Datenbanktechnisch habe ich eine Tabelle Customers und eine Tabelle Products (n:m Beziehung).
Mein ViewModel bildet einen Customer ab. In der ListBox möchte ich nun quasi die Verbinding herstellen zischen diesem Customer und der Products Tabelle, allerdings kann ein Product mehrfach verknüpft werden. Aber in seinen Eigenschaften nicht verändert, d.h. das Product-Objekt muss immer gleich bleiben!
D.h. für mein Programm, in der ComboBox stelle ich alle Products zur Verfügung die in der Products Tabelle stehen, ich wähle davon eines aus und genau dieses Product muss dann in die Products-ObservableCollection rein, die an die ListBox gebunden ist. Ändere ich die Auswahl in der ComboBox, dann muss in der ObservableCollection eigentlich genau dieses Objekt ausgetauscht werden.
Und genau hier hänge ich, also dass meine Änderungen an der ComboBox nicht in die ObservableCollection übergehen...
Ich habe auch ein Command, das einen neuen Eintrag (erst mal null) in der ObservableCollection erstellt. Dieser wird dann auch gleich als neues Item mit ComboBox (ohne Auswahl) in meiner ListBox angezeigt. Aber wenn ich dann was in der ComboBox auswähle, dann wandert diese Auswahl nicht in die ObservableCollection zurück, dort bleibt das vorher eingefügte null stehen...aber auch wenn ich statt null ein (existierendes) Dummy-Product in die ObservableCollection eintrage und im View ein anderes Product in der Combo auswähle, ich bekomme im ViewModel rein garnichts davon mit, die ObservableCollection wirf keine Events (ich hätte hier ein Replace erwartet).
Warum weiß ich nicht, und ich finde auch beim googlen keine Lösung, obwohl ich denke, dass das was ich suche doch ein gängiges Anwengsszenario sein sollte.
Wie mir da ein ViewModel helfen kann, das verstehe ich nicht. "." sollte doch schon der DataContext des ListBoxItems sein, oder?
Das mit dem Delete-Button wird recht trivial, wenn der Rest mal geht.

gruß
sth_Weird

28.07.2015 - 11:01 Uhr

Hallo,
ich habe in meinem Customer-ViewModel eine ObservableCollection<Product> Products. Außerdem gibt es noch eine Liste List<Product> AllProducts die alle Produkte liefert (hieraus kann sich der Customer quasi seine Products auswählen).
Die Producs Liste binde ich im View an eine ListBox. Die einzelnen ListBoxItems möchte ich als ComboBoxen anzeigen (hierfür habe ich ein DataTemplate definiert), ItemsSource der ComboBox soll immer AllProducts sein, SelectedItem dann halt der aktuelle Eintrag des ListBoxItems (also das Element aus Products das zu dem ListBoxItem gehört).
Leider bekomme ich das nicht zum Laufen...Ich bekomme es soweit hin, dass ich eine ComboBox sehe und diese AllItems anzeigt. Aber wie bekomme ich es jetzt hin, dass der ListBoxItem auch der SelectedItem der ComboBox ist, und noch wichtiger, wenn ich in der ComboBox einen anderen Eintrag auswähle, soll dieser sich ja auch in der ObservableCollection niederspiegeln. Das tut es zur Zeit nicht (ich habe mich an die CollectionChanged der ObservableCollection angehängt, dort fliegt kein Event, wenn ich einen anderen Eintrag in der ComboBox auswähle 😦 )
Hier ein Teil meiner XAML, hier habe ich versucht SelectedItems auf . zu setzen. da das ja das aktuelle Binding ist oder???


<ListBox Name="LbProducts" ItemsSource="{Binding Path=Products}" Margin="10,2,5,2" HorizontalAlignment="Left" VerticalAlignment="Center" Width="400">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <Grid Margin="0,2">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="50" />
                                </Grid.ColumnDefinitions>
                                <ComboBox Name="CboAllProducts"  Grid.Column="0" ItemsSource="{Binding Path=DataContext.AllProducts, ElementName=LbProducts}" SelectedItem="{Binding Path=., Mode=TwoWay}" />
                                ..hier soll noch ein Button zum Löschen hin                            </Grid>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>

hier ein Teil des Products-Properties


public ObservableCollection<Product> Products
    {
      get
      {
        if (this.products == null)
        {
          this.products= new ObservableCollection<Products>(myModel.Products);
          this.products.CollectionChanged +=
            delegate(object sender, NotifyCollectionChangedEventArgs e)
            {
              switch (e.Action)
              {
                 // hier ist Code drin, der bei Aendungen der ObservableCollection das Model anpassen soll...wie geschrieben der Debugger springt hier nie rein, wenn ich in der Combo ein anderes Element auswähle...

Die AllProducts Liste wird vom Konstruktur erzeugt und bleibt immer gleich.

Kann mir jemand sagen, wo der Fehler liegt???

grüße & danke
sth_Weird

28.07.2015 - 10:43 Uhr

Schade, dass da nichts zu machen ist..
Nuja, aber zu wissen dass etwas nicht (zumindst nicht so einfach) geht, das bringt einem auch schon mal etwas weiter 😉

Ich hätte noch die Idee, mich von ComboBox abzuleiten, mich dort an die KeyPresses zu hängen und die Zeit mit Timer zu überwachen...aber kann ich in der Ableitung überhaupt die eingebaute Filter-Funktion manipulieren/überschreiben?
Oder ich bau mir ne eigene ComboBox aus Textbox, "v"-Button und Popup...aber das wäre auch suboptimal.

gruß & danke

22.07.2015 - 08:53 Uhr

Hallo,

ich verwende in meinem Formular eine WPF ComboBox, die Items und das SelectedItem sind an Properties des ViewModels gebunden.
Wenn ein User die ComboBox öffnet und Buchstaben eintippt, dann springt die ComboBox automatisch zum nächsten Eintrag, der mit den eingetippten Buchstaben beginnt. Wird für eine "längere" Zeit nichts getippt und der User fängt erneut zu tippen an, dann nimmt die ComboBox an, es fängt ein neues Suchwort an. Das ist das Standard-Verhalten der WPF ComboBox, das man hat, ohne eine Zeile Code programmieren zu müssen.

Diese Zeitspanne, die maximal zwischen dem Tippen zweier zusammengehöriger Buchstaben vergehen darf, ist manchen Usern leider zu kurz (quasi für User, die mit der Tastatur noch nicht so gut vertraut sind und einzelne Buchstaben suchen müssen). Ist es möglich, die Zeitspanne zu vergrößern?

Wenn jemand einen treffenderen Titel findet, darf ihn gerne anpassen, mir ist nichts aussagekräftigeres eingefallen, ohne die maximale Anzahl der Buchstaben zu überschreiten.

Gruß & Danke
sth_Weird

09.07.2015 - 09:06 Uhr

Nuja das ganze hat eigentlich in dem Sinne kein ViewModel, es ist ein CustomControl das ein DependencyProperty "MyFlags" hat. Ich verwende es in einem Formular, und binde dort das MyFlags DependencyProperty an das ViewModel des ganzen Formulars. Ich hätte die Logik nun ungern im ViewModel des Formulars untergebracht, da sie ja an jeder Verwendungsstelle des CustomControls gilt.
Als einzig andere Alternative fällt mir dann aber ein, in meinem CustomControl noch ein DependencyProperty einzufügen das ich an die ungebundene Checkbox hefte. Leider wäre dieses Property dann auch nach außen sichtbar an allen Verwendungsstellen und könnte gebunden werden. Das würde mir nicht so gut gefallen...Oder gibt es DependencyProperties, dich im im DataTemplate des CustomControls binden kann, aber nicht an der Verwendungsstelle?
Oder doch mit Events arbeiten??? Es ist ja ein abgeschlossenes CustomControl da dürfte das ja eigentlich MVVM-technisch nicht so tragisch sein...

gruß
sth_Weird

08.07.2015 - 12:56 Uhr

hallo,

ja, aber ich möchte ja das IsChecked auf true setzen (was dann den Wert des Properties im hinterlegten Binding aktualisieren soll)...
Also ganz einfaches Beispiel: ich habe 2 Checkboxen die gebunden sind an 2 boolsche Werte des ViewModels. Eine dritte ungebundene Checkbox soll, wenn IsChecked = true wird, diese beiden Checkboxen auch IsChecked = true setzen. So ne Art "alles anwählen".
Da die Checkboxen bzw. IsChecked aber gebunden ist scheint das nicht zu gehen. Nimmt man das IsChecked-Binding raus tut's, aber wie soll der Wert dann zurück ins ViewModel wandern, wenn er nicht gebunden ist?.

Als Gegenbeispiel der umgekehrte Fall, wenn beide gebundene Checkboxen IsChecked=true sind, sollte die "alles anwählen" Checkbox automatisch auch angewählt sein.
Das bekommt man über einen DataTrigger prima hin, denn die Ziel-Checkbox ist ja nicht gebunden und somit kann man deren IsChecked im Setter des DataTriggers überschreiben.

gruß
sth_Weird

08.07.2015 - 10:45 Uhr

Ok das war's, danke...Das IsChecked = false sollte der Defaultwert sein.

Ich habe jetzt auch noch einen anderen Fall, da ist IsChecked gebunden (und hat Converter und ConverterParameter).
Aus eurer Begründung der Antworten leite ich dann aber ab, dass ich den Wert von IsChecked hier auch nicht so wie von mir angedacht über einen Trigger ändern kann, oder??? Wie müsste ich dann vorgehen, wenn ich das IsChecked setzen will, aber das Binding beibehalten (der Trigger soll quasi das gleiche auslösen, als hätte ich manuell auf die CheckBox geklickt, d.h. der Wert der gebundenen Property soll sich somit auch aktualisieren)? Wie gesagt das Binding von IsChecked bindet ein Property des ViewModels, hat Converter und ConverterParameter.
Direkt im Setter das gebundene Property setzen geht ja auch nicht:

<Setter Property="{Binding Path=MyFlags, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource FlagsToBoolConverter}, ConverterParameter=FlagXYZ}" Value="False"></Setter>

wirft zur Laufzeit ne Exception.
Mir fallen als Lösung nur ein, jedes einzelne Flag als DependencyProperty meines CustomControls zu implementieren und im setzen/rücksetzen die Sonderaktionen durchzuführen, die ich eigentlich mit den DataTriggern abhändeln wollte. Damit ist allerdings die Idee futsch, dass mein CustomControl nur die Gesamtmenge der Flags als DependencyProperty anbietet und nicht jedes einzelne.
Oder ich machs mit Events, ist aber sehr böse nach MVVM und auch nicht testbar...

Gruß & danke
sth_Weird

07.07.2015 - 13:19 Uhr

Hallo,
Ich habe ein Control mit n+1 Checkboxen. Eine Checkbox hat die Funktion "Keine Auswahl", die anderen n sind die "Auswahlmöglichkeiten". Die n Checkboxen für die "Auswahlmöglichkeiten" sind gebunden (etwas Background-Info: gebunden an Flags diverser Flag-Enums, dieser Part funktioniert bereits einwandfrei). Die Checkbox "Keine Auswahl" ist nicht gebunden. Ich möchte folgendes haben: Wenn eine oder mehrere IsChecked der n Auswahlmöglichkeit-Checkboxen auf true sind, dann soll sich die IsChecked der "Keine Auswahl" Checkbox automatisch auf false setzen, aber wenn keine der anderen Checkboxen IsChecked=true sind, dann soll sie sich automatisch auf true setzen.

das probiere ich:

<CheckBox Grid.Column="0" Grid.Row="1" Name="cbNothingSelected" IsChecked="False" Content="Keine Auswahl" Margin="0,0,5,0" VerticalAlignment="Center">
                            <CheckBox.Style>
                                <Style TargetType="CheckBox">
                                    <Style.Triggers>
                                        <MultiDataTrigger> 
                                            <MultiDataTrigger.Conditions>
                                                <Condition Binding="{Binding ElementName=cbAuswahl1, Path=IsChecked}" Value="False" />
                                                <Condition Binding="{Binding ElementName=cbAuswahl2, Path=IsChecked}" Value="False" />
                                                <Condition Binding="{Binding ElementName=cbAuswahl3, Path=IsChecked}" Value="False" />
                                            </MultiDataTrigger.Conditions>
                                            <Setter Property="IsChecked" Value="True"></Setter>
                                        </MultiDataTrigger>
                                        <DataTrigger Binding="{Binding ElementName=cbAuswahl1, Path=IsChecked}" Value="True">
                                            <Setter Property="IsChecked" Value="False"></Setter>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding ElementName=cbAuswahl2, Path=IsChecked}" Value="True">
                                            <Setter Property="IsChecked" Value="False"></Setter>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding ElementName=cbAuswahl3, Path=IsChecked}" Value="True">
                                            <Setter Property="IsChecked" Value="False"></Setter>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </CheckBox.Style>
                        </CheckBox>
                        <CheckBox Grid.Column="0" Grid.Row="2" Name="cbAuswahl1"...

Also der MultiTrigger soll prüfen ob die Auswahl-Checkboxen alle IsChecked=false haben und falls ja das eigene IsChecked auf true setzen.
Unten sind drei DataTrigger, diese sollen das IsChecked der Checkbox auf false setzen, wenn eine der Checkboxen IsChecked=true haben. 3 DataTrigger da ich damit die OR Beziehung abbilden will, der Multitrigger verknüpft die Conditions ja mit AND.

Es passiert: nichts. Ich kann wild auf den Checkboxen rumklicken, die "Keine Auswahl" Checkbox wird nie automatisch gesetzt/rückgesetzt so wie ich es erwarten würde.
Es werden keine Warnungs oder Fehlermeldungen ausgegeben. Compiliert fehlerfrei, es fliegen keine Exceptions. Die Trigger scheinen einfach nur ignoriert zu werden.
Sieht hier jemand spontan das Problem? Habe bisher noch nicht viel mit Triggern gearbeitet, wenn dann war es nur sowas wie Schriftart fett wenn selektiert oder so (hat bisher immer funktioniert)...

gruß & danke
sth_Weird

08.05.2015 - 08:59 Uhr

Danke schonmal für deine Antwort, mein Memento arbeitet intern auch mit einer Kopien des Originalobjekts. Nach außen soll sich die Instanz selber aber nicht ändern.Die Memento-Funktionalität ist bei mir bewusst im Model angesiedelt.
Vielleicht kann jemand noch was genaueres zur Funktionsweise und Reihenfolge sagen...ich wüsste leider nicht wie (verlässlich) testen, außer irgendwelche Debug-Ausgaben einzuführen...
Alternativ wäre es natürlich möglich eine Art Funktion UpdateAll() zu implementieren, die alle PropertyChanged in der Reihenfolge aufruft wie man sie in der Oberfläche braucht, aber wenn das mit dem null schon das gleiche macht, wäre das unnütz und außerdem fehleranfällig...

gruß
sth_Weird

07.05.2015 - 13:25 Uhr

Hallo,
ich habe in meiner Applikation das MVVM Pattern verwendet, meine ViewModels implementieren INotifyPropertyChanged.
In die Models haben ich Memento Support eingebaut, wenn ich im View dann auf Abbrechen klicke dann wird im ViewModel der Cancel-Command gefeuert, dieser ruft im Model eine RestoreMemento() Funktion auf die die Werte zurücksetzt. Das führt natürlich nun dazu, dass die Werte in der Oberfläche auch aktualisiert werden müssen... Korrekt müsste nun für alle Properties im VM die im View verwendet werden das NotifyPropertyChanged aufgerufen werden, damit das View mitkriegt, dass sich was geändert hat.
Laut MSDN wird dies auch gemacht (also laut Hilfe "alle Properties"), wenn man beim NotifyPropertyChanged Event in die EventArgs null oder einen leeren String als Parameter übergibt, anstelle des Namens des Properties.
Dazu ein paar Fragen, die leider in bei MSDN nicht beantwortet wird und zu der ich auch nichts weiteres gefunden habe:

  • betrifft das nur die Properties, die im View auch verbunden sind (zur Laufzeit weiß die Anwendung ja welche das sind), oder alle Properties, die in der VM Klasse sind (und einen getter haben)?
  • Wie ist die Reihenfolge, in welcher die Events gefeuert werden? Alphabetisch, zufällig oder in der Reihenfolge, wie sie im View verbunden sind?

gruß & thx
sth_Weird

23.04.2015 - 10:11 Uhr

nach Stunden vergeblicher Suche und Probieren hatte ich aufgegeben, nun hab ich kaum hier gepostet und prompt doch noch selbst bzw. auf einer bisher unentdeckten Seite eine Lösung gefunden (nuja so weit war ich ja garnicht entfernt, mein Style und das Ziel waren nur fehlplatziert und das ItemContainerStyle-Element hat außenrum gefehlt...)


<TabControl Grid.Row="8" Grid.Column="1" ItemsSource="{Binding Path=Components, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Margin="10,2,5,2" HorizontalAlignment="Left" VerticalAlignment="top">
                <TabControl.Resources>
                    <DataTemplate x:Key="Type1Template">
                        ...                    </DataTemplate>
                    <DataTemplate x:Key="Type2Template">
                        ...                        </DockPanel>
                    </DataTemplate>
                    </TabControl.Resources>
                    <TabControl.ItemContainerStyle>
                    <Style TargetType="TabItem">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Path=ChildType}" Value="Type1">
                                <Setter Property="TabItem.HeaderTemplate" Value="{StaticResource Type1Template}" />
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Path=ChildType}" Value="Type2">
                                <Setter Property="TabItem.HeaderTemplate" Value="{StaticResource Type2Template}" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </TabControl.ItemContainerStyle>
            </TabControl>

vielleicht brauchts ja irgendwann mal jemand...

gruß
sth_Weird

23.04.2015 - 09:49 Uhr

Hallo,

Kurzes Vorwort zu den Rahmenbedingingen: WPF Projekt, MVVM.
ich habe ein TabControl dessen ItemsSource an eine ObservableCollection<...> meines ViewModels gebunden ist. In der ObservableCollection können diverse Unter-ViewModels stecken, aber alle ViewModels haben hier die eine Gemeinsamkeit, dass sie ein Interface implementieren und daher ein Property ChildType (dahinter steckt eine Enumeration) haben.
Nun besteht ein TabItem ja aus einem Header und dem eigentlichen Inhalt. Im Moment habe ich es so, dass der Inhalt automatisch richtig angezeigt wird, da ich in den Resourcen DataTemplates hinterlegt habe für die jeweiligen ViewModels. Die Anzeige des Headers war bisher immer gleich, deshalb hatte ich das ganze so (gekürzt):


<TabControl Grid.Row="8" Grid.Column="1" ItemsSource="{Binding Path=Components, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Margin="10,2,5,2" HorizontalAlignment="Left" VerticalAlignment="top">
<TabControl.ItemTemplate>
                     <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="{Binding Number}" />
                            <TextBlock Text="{Binding Text}" />
                        </StackPanel>
                    </DataTemplate>
                </TabControl.ItemTemplate>
            </TabControl>

Das hat funktioniert. Nun aber eine neue Anforderung: Abhängig vom Wert der ChildType-Property soll für das ItemTemplate ein anderes DataTemplate verwendet werden. Dachte erst das wäre ganz einfach und ich könnte einfach einen DataTrigger verwenden. Versucht, Compilerfehler...Unterhalb TabControl.ItemTemplate (FrameworkElement) kann man nur EventTrigger verwenden 😦
Wo muss ich nun den DataTrigger hinschreiben, damit es geht?
Ich habe die beiden DataTemplates schon mit keys in den TabControl-Resources angelegt. Wo muss ich nun den DataTrigger unterbringen, damit das richtige DataTemplate im Header angezeigt wird?
Ich habe schon versucht ein Style zu erstellen für TabItem und dort im Setter das TabItem.ItemTemplate zu überschreiben, hat aber leider nicht funktioniert 😦
Hier das Style in den TabControl Resources, in der Variante habe ich natürlich das TabControl.Item Template von oben rausgenommen:


<Style TargetType="TabItem">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Path=ChildType}" Value="Type1">
                                <Setter Property="TabControl.ItemTemplate" Value="{StaticResource Type1Template}" />
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Path=ChildType}" Value="Type2">
                                <Setter Property="TabControl.ItemTemplate" Value="{StaticResource Type2Template}" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>

Leider passiert dann garnichts bzw. das allgemeine ViewModel-DataTemplate wird nun sowohl für die Header als auch für den Content verwendet, als wäre das Style garnicht da oder würde nicht angewandt.
Schreibe ich das TabControl.ItemTemplate von oben wieder dazu, wird es wie zuvor immer so angezeigt.
Kann mir jemand sagen wo ich den Trigger hinpacken muss damit es funktioniert??? Wie gesagt das betrifft nur den Header der Inhalt soll weiterhin wie bisher automatisch angezeigt werden. Und das Template muss auch explizit mit key ausgewählt werden und es sind auch total unterschiedliche Templates, es wird nicht nur eine Farbe oder so ausgetauscht (sonst könnte man den Trigger ja direkt an das Control hängen...).

gruß & danke
sth_Weird

13.03.2015 - 10:45 Uhr

Danke für die weiteren Tipps, solange sich aber keiner daran stört, dass der Speichern-Button immer enabled ist, werde ich es dabei lassen (ist die einfachste Lösung).

grüße
sth_Weird

03.03.2015 - 12:11 Uhr

Danke für deine Antwort. Möglich wäre das wahrscheinlich schon, aber es macht mir den Code für meinen Geschmack zu kompliziert (und die asynchrone Prüfung muss ja auch fertig sein, bevor ein schneller Klicker den Speichern Button angeklickt hat).
Ich löse es jetzt so, dass der Speicher Button immer enabled ist, aber er speichert halt nur wenn keine Fehler vorhanden sind.

trotzdem danke!
sth_Weird

20.02.2015 - 10:49 Uhr

Hallo,
ich stehe vor folgendem Problem und weiß nicht so recht, wie ich es am geschicktesten lösen kann (denke aber, dass doch schon mehrere Leute darüber gestolpert sein sollten???)

Ich habe ein View mit TextBoxen und einem Speichern-Button und dahinter ein ViewModel mit IDataErrorInfo etc. implementiert. Der Speichern-Button ist nur enabled wenn das ViewModel keine Fehler mehr hat. Nun ist es aber so, dass die Textbox ja durch den Default-UpdateSourceTrigger den Wert erst ins ViewModel übernimmt, wenn sie den Fokus verliert. D.h. aber, wenn ich den Wert einer fehlerhaften TextBox korrigiere, kann ich nicht direkt auf Speichern klicken, weil der Fokus ja noch auf der TextBox ist und das ViewModel folglich nichts von der Korrektur weiß, sich noch im Fehler-Zustand befindet und den Speichern-Button noch sperrt. Erst wenn ich den Fokus von der TextBox runternehme wird der Speichern-Button enabled da dann auch der Wert im ViewModel aktualisiert ist. Dieser gezwungene Fokuswechsel ist aber überhaupt nicht benutzerfreundlich 😦 Ich denke, ich kann doch nicht die einzige sein, die über dieses Problem gestolpert ist???) Außerdem gibt es ja den negativen Fall auch: Das ViewModel ist noch richtig aber im View wurde was falsches eingegben: der Speicher-Button kann direkt geklickt werden 😦 Natürlich prüft man in der Speichern-Funktion nochmal ob wirklich kein Fehler da ist 😉

Alternativen wären, den Wert durch Ändern des UpdateSourceTriggers immer direkt ins ViewModel zu übernehmen, dann würde aber quasi bei jedem eingegebenen Zeichen die Fehlerprüfung losgehen (die schon mal so lange gehen kann, dass der Anwender eine klitzekleine Verzögerung bemerkt, welche bei Fokuswechsel nicht weiter relevant ist, wenn sie bei jedem eingegebenen Zeichen auftritt aber doch stört).
OOODER
Ich lasse den Speicher-Button einfach immer enabled und prüfe halt beim Klicken ob Fehler vorhanden sind und breche ggf. das Speichern ab (Die Fehlermeldungen stehen ja sowieso auch auf dem Formular).
Das ginge schon auch, aber schöner ist es natürlich, wenn man den Button erst garnicht anklicken könnte.

Gibt es noch weitere Alternativen?

Gruß & danke
sth_Weird

12.02.2015 - 11:36 Uhr

Ich mag ja nicht im BaseViewModel (das ich noch an zig anderen Stellen verwende) überprüfen, ob in irgendeinem abgeleiteten ViewModel irgendein Interface implementiert wird. Auslagern, ja das ginge.

Ich habe es jetzt aber auch mit ein paar kleinen Änderungen rein in XAML implementiert bekommen. Dieser "Parent"-Fehler lag daran, dass ich statt <StackPanel.ContextMenu> nur geschrieben habe <ContextMenu>, dann wurde dieses selbstverständlich als unterlagertes Control interpretiert, und das wiederum geht bei ContextMenues natürlich nicht.

So geht es richtig: 😉
Anstatt eines ContextMenues als Resource zu definieren, definiere ich nur ein bzw. mehrere Arrays mit MenuItems


<x:Array x:Key="IEditableMenuItems" Type="{x:Type system:Object}" >
        <MenuItem Header="Editieren" Command="{Binding Path=DataContext.EditCommand}" CommandParameter="{Binding }">
            <MenuItem.Icon>
                <Image Source="{StaticResource IcoEdit}"  Width="16" Height="16"></Image>
            </MenuItem.Icon>
        </MenuItem>
...

Dann kann ich mehrere auf diese Art und Weise angelegten Arrays in einem ContextMenu zusammenfassen:


<StackPanel Orientation="Horizontal">
            <StackPanel.ContextMenu>
                <ContextMenu DataContext="{Binding PlacementTarget,RelativeSource={RelativeSource Self}}">
                    <ContextMenu.ItemsSource>
                        <CompositeCollection>
                            <CollectionContainer Collection="{StaticResource IEditableMenuItems}" />
                            <CollectionContainer Collection="{...}" />
                        </CompositeCollection>
                    </ContextMenu.ItemsSource>
                </ContextMenu>
            </StackPanel.ContextMenu>

Kommt halt drauf an was man braucht und wo oder wie man das ganze einsetzt. Ich sehe in deiner Lösung auch viele Vorteile und habe ähnliches schon in WinForms in anderen Projekten implementiert, leider kann ich diese Vorteile aber in meinem aktuellen Projekt nicht nutzen, deshalb bleibe ich bei meiner einfachen XAML Lösung.
Dennoch vielen Dank und wenn jemand das gleiche Problem hat, dann kann er ja überlegen, welcher Ansatz für ihn besser passt.

gruß
sth_Weird

11.02.2015 - 08:41 Uhr

Vielen Dank für deine Antwort. Das wäre natürlich auch eine Möglichkeit.
Im Moment schreckt mich noch die viele Schreibarbeit ab (die sich dann ja qualsi auch in jedem ViewModel mehr oder weniger wiederholt).
Ich warte mal noch etwas ab, ob jemand doch noch ne Lösung im XAML findet, falls nicht, werde ich es so implementieren. Vielleicht könnte man das ganze ja so vereinfachen, dass man sich eine Helper-Klasse schreibt, die für jedes Interface die MenuItemViewModels liefert (so in der Art GetMenuItemViewModel(IMyInterface obj)), dann muss man sich das Menu nicht in jedem ViewModel aufbauen (und dann ist es auch wieder weniger Arbeit)...

Gruß
sth_Weird

10.02.2015 - 13:11 Uhr

Hallo,

ich habe in meiner Anwendung diverse ViewModels, die wiederum diverse Interface implementieren. Diese Interfaces legen u.a. auch fest, dass das ViewModel bestimmte Funktionen (Commands) bereitstellen muss, die man dann im View aufrufen kann.
Es gibt verschiedene DataTemplates für die Anzeige der ViewModels. Innerhalb dieser DataTemplates möchte ich auch ContextMenues anzeigen die an die Commands des ViewModels gebunden sind. Da das ContextMenu ja eigentlich Interface-spezifisch ist (wenn ein ViewModel Interface1 implementiert soll es bestimmte Menueinträge bereitstellen, wenn es Interface2 implementiert wiederum andere), habe ich das ContextMenu als Resource angelegt. An der Verwendungsstelle verweise ich nur darauf.

Grob das was ich gemacht habe (funktioniert!!!).

Mein ContextMenu, als statische Resource in XAML definiert:

<ContextMenu x:Key="CtxIEditableViewModel" DataContext="{Binding PlacementTarget,RelativeSource={RelativeSource Self}}">
        <MenuItem Header="Editieren" Command="{Binding Path=DataContext.EditCommand}" CommandParameter="{Binding }">
            <MenuItem.Icon>
                <Image Source="{StaticResource IcoEdit}"  Width="16" Height="16"></Image>
            </MenuItem.Icon>
        </MenuItem>
  ...

Hier eine Verwendungsstelle in der selben Resource-Datei, das ist hier ein DataTemplate für ein ViewModel:


<HierarchicalDataTemplate DataType="{x:Type viewModelData:MyDataViewModel}" ItemsSource="{Binding Path=Children}">
        <StackPanel Orientation="Horizontal" ContextMenu="{StaticResource CtxIEditableViewModel}">
            ...Anzeigeelemente...
      </StackPanel>
    </HierarchicalDataTemplate>

Funktioniert alles wunderbar.

Problem:
Wenn ein ViewModel zwei Interfaces implementiert, dann möchte ich natürlich in meinem ContextMenu alle Aktionen beider Interfaces anzeigen, d.h. ich müsste zwei ContextMenus mergen. Das krieg ich leider nicht hin 😦
So habe ich es probiert:


<HierarchicalDataTemplate DataType="{x:Type viewModelData:MyComplexDataViewModel}" ItemsSource="{Binding Path=Children}">
    <StackPanel Orientation="Horizontal">
            <StackPanel.ContextMenu>
                <StaticResource ResourceKey="CtxIEditableViewModel" />
                <StaticResource ResourceKey="CtxIAdminViewModel" />
            </StackPanel.ContextMenu>
           ...Anzeige-Elemente...

das geht auch nicht:


<HierarchicalDataTemplate DataType="{x:Type viewModelData:MyComplexDataViewModel}" ItemsSource="{Binding Path=Children}">
        <StackPanel Orientation="Horizontal">
            <ContextMenu>
                <ContextMenu.ItemsSource>
                    <CompositeCollection>
                        <StaticResource ResourceKey="CtxIEditableViewModel" />
                        <StaticResource ResourceKey="CtxIAdminViewModel" />
                    </CompositeCollection>
                </ContextMenu.ItemsSource>
            </ContextMenu>

Fehlermeldung in beiden Fällen:
"&quot;ContextMenu&quot; kann keine logischen oder visuellen übergeordneten Elemente besitzen."}

Kann mir jemand sagen, wie ich es schreiben muss, damit es geht???
Wenn ich nur ein ContextMenu verwende geht es ja, deshalb ist mir die Fehlermeldung schleierhaft, wo kommt plötzlich ein übergeordnetes Element her, wenn ich ein zweites ContextMenu quasi als Sibling dazumachen will, bei einem ContextMenu kommt der Fehler ja nicht?

Danke!
Gruß
sth_Weird

28.01.2015 - 08:56 Uhr

Hallo,

so bzw. so ähnlich hatte ich es ganz am Anfang (nur mit einem UserControl weil mir die Idee mit dem ItemsTemplate nicht gekommen war). Hier hatte ich dann aber das Problem, dass ich in meinem HauptViewModel ein FlagsViewModel hatte. Da das Flags-Property das ich darin kappsle ja aber ein Wertetyp und kein Verweistyp ist, sind die Änderungen nie zurück ins HauptViewModel bzw. Model gewandert, es wurde nur der Wert im FlagsViewModel angepasst (eigentlich logisch, aber dieser Fehler war echt schwer zu finden). Und mit ref kann ich ja ein Property nicht übergeben (meckerte jedenfalls der Compiler). Ich musste also immer irgendwie vor dem Speichern der Änderungen dafür sorgen, dass der Wert von meinem FlagsViewModel händisch wieder ins HauptViewModel bzw. Model wanderte.

Gruß
sth_Weird

28.01.2015 - 08:39 Uhr

Danke, da macht das ganze natürlich etwas kürzer, aber da muss ich dann ja trotzdem bei jeder TextBox daran denken, dass ich was spezielles dazuschreibe...geht das denn wirklich nicht im Style festzulegen?

27.01.2015 - 13:00 Uhr

kaum stellt man die Frage findet man doch was dazu
--> zu meiner zweiten Frage:
Im Style muss man fürs Binding die RelativeSource auf TemplatedParent setzen. Wichtig hier: TemplateBinding funktioniert nur OneWay, egal wie man den Mode setzt. Wenn man also TwoWay braucht dann muss man es "lang" schreiben, also mit normalem Binding und RelativeSource. Vielleicht hat ja mal jemand das selbe Problem...

27.01.2015 - 12:20 Uhr

Sorry, aber wie meinst du das? Im TextBox Style gibt es keine Setter für das Text-Binding, wo ich was überschreiben könnte...

27.01.2015 - 12:14 Uhr

Hallo,

in meinem ViewModel habe ich ein Property dessen Typ ein Flag-Enum ist.
Ich würde den Wert im View gerne darstellen als eine (sinnvoll angeordnete) Gruppe von Checkboxen. Da ich das ganze an mehreren Stellen brauche, würde ich es gerne in ein eigenes Control packen.
Ich bin für alternative Vorschläge offen, hier ist aber erstmal das was ich mir gedacht habe:
-> CustomControl mit DependencyProperty "MyFlags" vom Typ des Flag-Enums
Im DefaultStyle des CustomControls definiere ich im ControlTemplate die Checkboxen. Hier muss ich nun irgendwie erreichen, dass die Werte der einzelnen Flags vom "MyFlags" in die IsChecked-Eigenschaften münden.
Hier hänge ich jetzt gerade.
Überlegt habe ich mir: Ich definiere im CustomControl (Dependency) Properties für die Flags, an welche ich mich im Style binden kann. Diese Properties müssen aber intern sein, ich möchte mich an diese von "außen" nicht binden können, da darf nur das MyFlags-Property selbst sichtbar sein!!!
**Erstens: Kann ich auch private Dependency Properties erstellen und kann ich mich an diese im Style dann binden?
Zweitens: Wie muss ich innerhalb des Styles das Binding überhaupt definieren, damit es das zu bindende Property dann in der eigenen = CustomControl- Klasse sucht (und nicht etwa in einem (vererbten) DataContext). **
Und die eigentlich wichtigste Frage dazu:
Ist das überhaupt die richtige Herangehensweise oder würdet ihr das anders lösen (wie)?

Die zweite Alternative die ich mir überlegt hatte war, anstelle der einzelnen Flags-Properties mit einem parametrierbaren Konverter zu arbeiten (d.h. der Parameter wäre dann das Flag)...

Danke im Voraus für eure Kommentare, Vorschläge, Hinweise, Bedenken etc. zu meinem Anliegen 😃

gruß
sth_Weird

27.01.2015 - 08:53 Uhr

hallo,

es scheint mir eingentlich eine einfache Aufgabe zu sein, aber ich bekomms nicht hin 😦

Ich habe in den Resourcen ein TextBox-Style definiert, dass die Validierungsfehler im Tooltip anzeigt. Das funktioniert, wenn ich für meine Textbox auch definiere, dass die Validierung eingeschaltet ist. Nicht selten vergesse ich das leider (vor allem wenn was neues dazukommt) und wundere mich, dass ich nichts angezeigt bekomme.
Mein erster Gedanke war nun, dass man das doch einfach auch im TextBox Style so einstellen können muss. Da gibt es aber leider kein Property ValidatesOnError oder Text.ValidatesOnError oder so. Bisher habe ich beim googlen auch keine Hilfe dazu gefunden. Viele Beispiele definieren zwar ein Style für die TextBox um den Fehler im ToolTip anzuzeigen, aber die Validierung wird immer für jede Textbox einzeln aktiviert. Muss ich das denn wirklich bei jeder Textbox in meinem Formular dazuschreiben, oder gibt es doch eine Möglichkeit, das irgendwie im Style zu definieren (oder einen sonstigen, einfachen Workaround)?

Ich mach mal noch etwas (gekürzten) Code dazu, damit es klarer wird was ich meine...

Das hier steht in meinen Resourcen

<Style TargetType="TextBox" BasedOn="{StaticResource ...}">
                <Setter Property="IsReadOnly" Value="{Binding Path=State, Converter={StaticResource VmStateToBooleanConverter}, ConverterParameter={StaticResource FalseValue}}"></Setter>
                <Style.Triggers>
                    <Trigger Property="Validation.HasError" Value="true">
                        <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},
                            Path=(Validation.Errors)[0].ErrorContent}" />
                    </Trigger>
                </Style.Triggers>
            </Style>

Damit das oben auf ALLE meine Textboxen im Formular angewandt wird, muss ich für JEDE Textbox schreiben:


<TextBox ... Name="..." Text="{Binding Path=..., Mode=TwoWay, [B]ValidatesOnDataErrors=True, ValidatesOnExceptions=True[/B]}" 

d.h. bei zehn TextBoxen in meinem Formular muss ich zehn mal dieses ValidatesOnDataErrors etc. schreiben und wenn ich es einmal vergesse, dann wird diese TextBox nicht validiert.
Was ich möchte ist, dieses ValidatesOnDataErrors etc. nicht zehn mal hinschreiben zu müssen, sondern einmal an zentraler Stelle (z.B. im Style wenn das geht)

IDataErrorInfo etc. ist alles "korrekt" implementiert, wie gesagt wenn ich dieses ValidatesOnBlabla dazuschreibe dann tut alles wie gewünscht. Ich suche also keinen Fehler oder so, sondern nur einen einfacheren Weg zum Ziel 😉

gruß & danke
sth_Weird

18.12.2014 - 11:08 Uhr

Hallo,

Mal wieder eine Prinzip-Frage...
Ich sehe in MVVM Tutorials oft zwei unterschiedliche Herangehensweisen, wenn ein ViewModel eine Liste mit komplexen (eigenen) Datentypen enthält.
Angenommen meine Model-Klasse A enthält eine Liste mit Objekten der Klassen B.
Ich hätte dann auf jeden Fall ein ViewModel Vm_A und darin bräuchte ich auch in irgendeiner Form eine Liste der Objekte der Klasse B.
Wenn man das Prinzip "das View kennt kein Model" wörtlich nimmt und streng durchzieht, müsste ich nun in meinem Vm_A eine ObservableCollection anbieten, die Objekte von Vm_B enthält.
Ich sehe aber oft, dass an dieser Stelle mit dem Model selber gearbeitet wird. Mein Vm_A bietet also eine Liste mit Objekten der Klasse B an (die Liste wird quasi einfach nur 1:1 aus dem Model selbst hochgereicht und nicht selbst in ein ViewModel gepackt).
Man sieht solchen Tutorials leider meist nicht an (auch nicht von den Bewertungen her, denn ein WPF-Anfänger frisst oft alles (und bewertet es gut) was ihm hingeworfen wird, hauptsache es funktioniert), ob der Urheber sich technisch echt auskennt oder einfach nur was zusammengebastelt hat.
Und wie gesagt...man sieht beide Varianten, ungefähr gleich oft würde ich sagen, meist mit sehr ähnlichen, einfachen Beispielen.

Wie macht man es richtig bzw. wie machen es die WPF Gurus hier?
Ich habe mir überlegt, dass es hier vielleicht auch wieder (wie so oft) vom Kontext abhängt...wenn ich die Objekte von B z.B. wirklich nur als Auswahl anzeigen will oder so, hätte ich ja in den meisten Fällen keinen Mehrwert, sie in einem eigenen ViewModel zu verpacken. Nur wenn ich mit dem Objekt von B auch etwas komplexeres vorhabe (z.B: auch editieren, komplexe Daten anzeigen etc.), würde ein eigenes ViewModel Vorteile bringen...???

gruß & danke schonmal für eure Gedanken dazu
sth_Weird

05.12.2014 - 11:38 Uhr

ich habs jetzt mal (da es mein erster Ansatz war und funktioniert) so gelassen wie du auch geschrieben hast, ich habe mein MainWindowViewModel und da gibt es dann den "SelectedItem", UserControl1 bindet sich daran (hier wird der Wert gesetzt) und UserControl2 bindet sich daran (hier wird der Wert verwendet).
Als Vorteil der 2.Variante mit dem ElementName etc. hätte ich mir jetzt vorstellen können, dass man für UC1 und UC2 zwei unterschiedliche, eigenständige ViewModels hätte setzen können (beides in Form von Properties des MainViewModels). So wie es jetzt ist muss ich für die "Kommunikation" der beiden UserControls dasselbe ViewModel verwenden da sie sich über dessen SelectedItem Property verbinden...Angenommen die UserControls sind etwas komplexer, würde sich ggf das MainViewModel ziemlich aufblähen, wenn es für alle UserControls alle möglichen/gebrauchten DependencyProperties vorhalten müsste (???).

gruß
sth_Weird

03.12.2014 - 12:18 Uhr

Hallo,

Ich habe ein Control=View mit einem ViewModel als DataContext.
auf diesem Control befinden sich zwei UserControls. Diese haben beide DependencyProperties desselben Typs, dessen Wert aus UC1 soll in UC2 enfließen.
In meinem ersten Ansatz habe ich beide Dependency Properties "SelectedItem" an dasselbe Property meines ViewModels gebunden. Das funktioniert.
Nun hab ich aber auch die Möglichkeit gelesen, in UC2 das Binding in der Form "ElementName MyUC1, Path=MyDependencyProperty" zu definieren. In dem Fall bräuchte ich den "Umweg" über das ViewModel nicht.
Was sind die Vor/Nachteile der beiden Varianten? Widerspricht die Lösung mit der Verknüpfung der UserControls mit ElementName=... etc. dem MVVM Prinzip da ich den SelectedItem nicht an mein ViewModel binde sondern direkt auf View Ebene verknüpfe?
Vorweg: beides scheint zu funktionieren 😉 Ich suche nur den "empfohlenen Weg" (falls es den gibt), damit später niemand fragt "warum hast du das denn nicht so gemacht?"

gruß & danke
sth_Weird

28.11.2014 - 10:56 Uhr

nur kurz zum Kontext: ist eine Art Master/Detail Sache, einmal verwende ich das ViewModel im Baum, einmal in der Detailanzeige (dort sollen natürlich alle Eigenschaften angezeigt werden, während im Baum nur ein Icon und ein Name reichen). Wichtig: nicht jeder Knoten im Baum hat das selbe ViewModel, das ist abhängig vom BO das drin steckt, d.h. ich habe mehrere DataTemplates!
Im Moment habe ich für beide Fälle einen DataTemplateSelector der ein Dictionary hält und nachschlägt welches DataTemplate für welchen Typ passt. Da muss ich im Moment halt alle Typen mit Key registrieren. Geht auch und ist auch recht simpel und gut erweiterbar (wenn man drandenkt neue DataTemplates zu registrieren 😉), aber anders wärs halt einfacher da ich mir die Zeilen Code ganz sparen könnte!
Danke für deine Antwort, ich werds ausprobieren, sobald ich wieder dazu komme!

gruß
sth_Weird

28.11.2014 - 10:39 Uhr

Ich bin zwischenzeitlich beim googeln eher zufällig auf eine generelle "Lösung" oder eher "Frage" gestoßen, die in meinem Fall prima gepasst hat. Die Lösung stammte sogar hier von dieser Seite aber leider habe ich es verpasst mir den Link zu merken und die Suche nach UserControl bringt mir zu viele Ergebnisse zum durchforsten welches es dann nun war 😉
Der Urheber darf sich hier gerne "outen" und den Originallink anhängen und ich sage vielmals Danke für seinen Gedankenanstoß...
Die Aussage oder besser gesagt Frage dieses Users war, warum denn ein UserControl überhaupt ein eigenes ViewModel haben sollte, da ja z.B. ein TextBox-Control oder Label-Control auch kein ViewModel hat und ein UserControl ja eigentlich nichts anderes ist als so ein Control nur halt komplexer. Man müsste einfach für das UserControl DependencyProperties rauslegen und diese ans MainViewModel binden (dieses muss dann halt die Properties dafür haben, aber ich binde sie ja eben genau deshalb, weil ich sie dort auch brauche!).
Die Wiederverwendbarkeit des UserControls ist damit trotzdem gegeben. Ich kann den Kontext ja auf irgendein ViewModel setzen, es ist nur wichtig, dass es die entsprechenden Properties zum Anbinden hat 😃

gruß
sth_Weird

28.11.2014 - 09:51 Uhr

Hallo,
Ich habe nur eine kurze theoretische Frage, die mir das Netz so nicht genau beantwortet. Leider kann ich es zur Zeit nicht "live" ausprobieren.
Wenn ich in meiner Anwendung ein DataTempate für einen bestimmten Typ ohne Key definiere, dann wird das ja an allen Stellen automatisch gesetzt (so ist die Aussage die man beim googeln überall findet, leider sind die Beispiele immer sehr trivial, da sie von einem einzigen ResourceDictionary ausgehen das quasi überall eingebunden wird).
Ist das wirklich immer so? Angenommen ich habe zwei ResourceDictionaries und definiere in beiden ein DataTemplate für den selben Typ aber ohne Key, und verwende das eine ResourceDictionary in einem UserControl und das andere in einem anderen. 1. Compiliert das überhaupt oder würde er da meckern, dass es für einen Typ zwei Default-Templates gibt? und 2. falls es compiliert, wird dann in beiden Fällen das richtige DataTemplate ausgewählt oder entscheidet das der Zufall. 3. angenommen die beiden Default-Templates für den gleiche Typ werden in zwei unterschiedlichen Dlls definiert (beide Dlls werden aber in derselben Anwendung verwendet), spielt das eine Rolle?

gruß & danke
sth_Weird

28.11.2014 - 09:41 Uhr

Ja, das mit dem Tag hab ich auch im Netz irgendwo gelesen, aber das finde ich unschön, da es das Property irgendwie "missbraucht".
Ein AttachedProperty fuer nen Button mag ich aber auch nicht, da ich die Eigenschaft ja nur für das eine Button-Style brauche und für andere Button-Styles nicht.
Da würde ich dann doch eher einen abgeleiteten Button erstellen und dort die Orientation (oder andere Eigenschaften die Kandidaten zum überschreiben wären) als DependencyProperty vorsehen...

gruß
sth_Weird

26.11.2014 - 13:05 Uhr

ja, das mit den zwei getrennten Styles ist der einfachste Weg, den ich im Moment mangels Zeit auch gehe, leider muss ich damit aber halt alles doppelt pflegen.
Trotzdem danke für deine Antwort!

Gruß
sth_Weird