C# Umsetzung GUI (StackPanel, Auf- und Zuklappbare Kategorien, Checkboxen,.. )

H1deAndSeek

Lieutenant
Registriert
Okt. 2009
Beiträge
895
Hallo zusammen,

inzwischen bin ein wenig besser mit C# vertraut. Aber mir fehlt dann doch die Kenntnis von den WPF Grundlagen.
Da ich leider nicht weiß, was ich genau brauche, lässt sich nur schwer danach googlen.

Ich möchte die folgende GUI nachbauen mit C# und WPF.
konzept.jpg

Funktionalität:
Bei dem Klick auf ein "-" (Minus) wird die Kategorie eingeklappt. Bei Klick auf "+" analog dazu, wird die Kategorie ausgeklappt.
Es gibt sowohl vor jedem "Namen" als auch vor jeder "Kategorie" eine Checkbox,die man auswählen kann.
Wenn man die Checkbox der Kategorie auswählt, dann werden alle Children Checkboxen ausgewählt.

Rein logisch sollte ich kein Problem haben, das zu implementieren, allerdings weiß ich nicht, wie ich das vom Prinzip her angehen soll.

Bisher hatte ich dabei an folgende Umsetzung gedacht:
Ich könnte pro Kategorie ein StackPanel benutzen, in dem dann die einzelnen "Namen" stehen. Die "Kategorien" müsste ich extra machen. + und - als Toggle-Button,dass das StackPanel ausblendet (geht des?). Checkbox der Kategorie als Button, die einfach alle Child Checkboxen checked/unchecked.

Edit: Achja, das ganze sollte noch in einer ScrollBox sein. Da es dann doch viele Einträge werden.


Ist dies so durchführbar? Gibt es elegantere Lösungen?

Vielen Dank und viele Grüße.

Ich hoffe ich hab nichts vergessen in meiner Fragestellung ;)
 
Zuletzt bearbeitet:
Wie wärs mit selbst implementierten Forms und custom Classes?
Dabei lernst du wesentlich mehr als mit WPF, auch wenn dieses grad zum neuen Standard mutiert :)

mfg,
Max
 
codeBehindDifferentToXml.jpgOkay.

So weit so gut.

Warum kann ich im Code Behind den Header eines TreeViewItems nicht genauso definieren wie in XML?

In XML kann ich dem Header noch ein StackPanel mit einer Checkbox geben. Im Code Behind geht dies nicht?
 
Zuletzt bearbeitet:
Ohne jetzt Code zu schreiben, würde ich behaupten doch das geht;

du musst
  1. StackPanel definieren
  2. Checkbox definieren
  3. Checkbox in das StackPanel legen
  4. StackPanel in den Header legen

Ist ein Schachtelsystem, im XML sieht es halt deutlich eleganter und leichtgewichtiger aus;
Denk dran jedes Teil ist ein Objekt: TreeView, jedes TreeViewItem, jedes StackPanel, jede Checkbox;
Alles was du mit new anlegst, ist was neues und anders und kennt sich im ersten Moment nicht;
mit Add schaffst du erst die Verbindung zu einander;
 
H1deAndSeek schrieb:
Warum kann ich im Code Behind den Header eines TreeViewItems nicht genauso definieren wie in XML?
Der überflüssige Punkt hinter "someName.Header" ist dir aufgefallen?

Ansonsten hat @M0rpHeU5 absolut recht.
"Header" ist vom Typ Object, das heißt du kannst darin praktisch beliebig viele Controls schachteln. Im Code ist das nur etwas verwirrend da du genau andersrum wie im XAML arbeiten musst: Im XAML deklarierst du das übergeordnete Element (z. B. TreeViewItem) und packst die untergeordneten hinein (z. B. StackPanel). Im Code definierst du erst die unterordneten Elemente und fügst diese danach in die überordneten ein.
Deshalb sollte man in der WPF (meiner Meinung nach) die Oberfläche soweit wie möglich in XAML machen.


max_1234 schrieb:
Wie wärs mit selbst implementierten Forms und custom Classes?
Dabei lernst du wesentlich mehr als mit WPF, auch wenn dieses grad zum neuen Standard mutiert :)
Ich wüsste nicht, warum man als Einsteiger ohne Not noch Custom Windows-Forms-Controls schrieben sollte. Mit XAML ist das viel einfacher (häufig kann man es sogar mit einem Control Template lösen) und tiefe GDI+-Kenntnisse bringen dir dabei außer einige graue Harre praktisch nichts.
 
Leider habe ich beim Header nur 4 nutzlose Methoden mit denen ich das StackPanel nicht in den Header legen kann.

Naja, habe es jetzt annders gelöst. Ist zwar nicht so elegant, kann aber später geändert werden.

@TheCadillacMan,
ja, den Punkt habe ich bemerkt. Eig. sollte der Screenshot noch zeigen, welche Methoden ich habe. Klar nur 4, weil es noch ein Object ist. Ich habe allerdings nicht herausbekommen, wie ich das in einen "Header" casten kann bzw. in ein TreeViewItem...
Dachte, dass ich es in ein FrameworkElement caste, aber des funktioniert nicht?

Ah okay. Wenn ich die Vererbungshierarchie anschaue, ich kann es in ein HeaderedItemsControl casten und habe dann .Items.Add zur Verfügung.


Jetzt kommt eine Frage, die glaube ich, deutlich schwieriger in der Umsetzung wird...


Ich möchte jetzt eine Live Suche implementieren!
Es gibt ein Input-Suchfeld in das Text eingegeben wird.
Es gibt ein Dictionary in dem Name gespeichert sind, sowie eine Beschreibung. Ich möchte sowohl im Namen, als auch in der Beschreibung nach dem Suchwort suchen.

Gibt es da etwas vorgefertigtes? Ich möchte die Suche ungern selbst implementieren ;_;

Also ich habe ein Dictionary mit:
("key", value)
Wobei value = Tupel ( "Name", "Beschreibung", Zahl)

Ich möchte jetzt in jedem Item in dem Das Suchwort entweder in Name oder in der Beschreibung zu finden ist, das Element zu einem neuen Dictionary hinzufügen. Da ich mir die GUI aus einem Dictionary erstellen lasse, passt des dann. (Ja, keine Liste, ich brauche einen Dictionary :P)

Code:
foreach (KeyValuePair<string, Tuple<string,string, int>> dicItem in Dictionary) 
{
    if (dicItem.Value.Item1.contains(searchString)  OR
                        dicItem.Value.Item2.contains(searchString))
   {
     someNewDictionary. Add ( dicItem)
    }
}

Ist das effizient und durchführbar mit einer Live Suche ?
Ok. Ändern wir die Suche von Live auf: nach klick auf einen Button...
 
Zuletzt bearbeitet:
H1deAndSeek schrieb:
ja, den Punkt habe ich bemerkt. Eig. sollte der Screenshot noch zeigen, welche Methoden ich habe. Klar nur 4, weil es noch ein Object ist. Ich habe allerdings nicht herausbekommen, wie ich das in einen "Header" casten kann bzw. in ein TreeViewItem...
Du willst doch den Header zuweisen. Das geht genauso wie bei "Name". Header ist vom Typ Object, d. h. du kannst jedes beliebige Objekt hineingeben.
Code:
TreeViewItem someName = new TreeViewItem();

// Das geht. Einfach zuweisen.
someName.Header = "Hallo!";

// Das aber auch.
StackPanel stackPanel = new StackPanel();
Label label = new Label(); // Statt einem Label kannst du natürlich auch eine CheckBox oder was auch immer reinpacken.
label.Content = "Hallo!";
stackPanel.Children.Add(label);
someName.Header = stackPanel;


H1deAndSeek schrieb:
Es gibt ein Dictionary in dem Name gespeichert sind, sowie eine Beschreibung. Ich möchte sowohl im Namen, als auch in der Beschreibung nach dem Suchwort suchen.
So kompliziert ist das nicht (zumindest nicht die naive Implementierung).

Ich nehme an du willst nach jedem eingebenden Buchstaben suchen?
Du brauchst eine TextBox. Diese hat ein Event names "TextChanged". Das wird jedes mal ausgelöst, wenn sich der eingegebene Text ändert. Im Event-Handler läufst du dann mit foreach über die Einträge in deinem Dictonary und prüfst ob Name oder Beschreibung übereinstimmen. Bei denen das der Fall ist kannst du die Daten dann ausgeben wie du willst (z. B. in einer ListBox oder einer zweiten TextBox).
Wenn du dich mit Events nicht auskennst, solltest du das dringend nachholen, da sie für die GUI-Programmierung unter .NET unbedingt nötig sind.
 
TheCadillacMan schrieb:
Code:
someName.Header = stackPanel;

Huch, wie habe ich denn verplant, dass das geht :D - Ich dachte das hätte ich ausprobiert und es ging nicht, was auch der Auslöser für die 2. Frage war. Vielen Dank auf jeden Fall.

TheCadillacMan schrieb:
So kompliziert ist das nicht (zumindest nicht die naive Implementierung). [...]

Events sind kein Problem. Mir ging es eher um die Effizienz. Der Name ist vmtl. im durchschnitt ~6 Wörter lang, während die Beschreibung bis zu ~100 Wörtern umfassen kann.

Ich Google mal ein wenig, wie effizient String.Contains(String) ist und schaue dann mal weiter.


Vielen Dank auf jeden Fall schon einmal und viele Grüße!
 
H1deAndSeek schrieb:
Mir ging es eher um die Effizienz. Der Name ist vmtl. im durchschnitt ~6 Wörter lang, während die Beschreibung bis zu ~100 Wörtern umfassen kann.
Effizient ist die naive Implementierung natürlich nicht.
Für Volltextsuche im großen Stil gibt es spezialisierte Frameworks (z. B. Lucene.Net, der Integrationsaufwand lohnt sich aber wohl erst bei ein paar Millionen Einträgen deiner Art.
 
Noch eine Frage, zu der ich keine Antwort ergooglen konnte.

Wie setze ich alle Children, Children-Children, Childre-C-C eines FrameWork Elements auf Visbility = Visible ?
Rekursion. Klar. Aber Gibt's da vllt. etwas von WPF?
 
Zuletzt bearbeitet:
Views filtern kann man per ICollectionView.Filter-Property.

Visibility für alle children setzen müsste klappen, wenn man child.Visibility an parent.Visibility bindet.
Dann muss man nur noch parent.Visibility entsprechend setzen und die children werden per binding aktualisiert.
 
Zu all dem gibt es nur eine Antwort wenn hier WPF verwendet wird! BITTE Bindings Benutzen. Wozu diese veraltete Denkweise, dass man irgend etwas manuell setzen muss! Hier sind keine WinForms drinne :D
 
DataBinding ist in WPF eigentlich das A&O; Aber wenn man als Einsteiger nicht mal das TreeView kennt, sollen wir ihn mit dieser Komplexität überfordern?

Meine Vorschlag an den TE
1. Stufe) TreeView einbauen (vll ist diese ja statisch)
2. Stufe) TreeView im XAML definieren
3. Stufe) DataTemplate Wissen aufbauen und DataBinding nutzen <-> PropertyChanged Event
4. Stufe) MVVM Pattern erlernen bzw. auf Frameworks setzen

PS: Sogar Winforms via Infragistics haben BindingSources ;-)
 
Infragistics ist aber nicht kostenlos ;)
Aber ja DataBinding will erlernt werden. Am besten mit MVC Pattern anfangen, dann kann man schon mit MVVM arbeiten.
 
Also, da hier ja wieder rege Beteiligung herscht:

Die Suche habe ich so umgesetzt, dass ich alle Checkboxen bei Erstellung in eine Liste packe und die dann einfach iterieren kann. Die Suche funktioniert auch relativ schnell, lediglich wenn wieder alle auf Visible gesetzt werden (man den Suchparameter entfernt) gibt es ~3 Sekunden delay. Nunja, muss man hinnehmen, wenn es so Quick und Dirty ist.

MVC kenne ich eigentlich sehr gut, aber irgendwie sagt mir das MVVM nicht so zu. Lt. Wikipedia ist es nur eine Art des MVC, trotzdem tue ich mich ein wenig schwer das in kurzer Zeit zu realisieren und komplett zu verstehen. Habe zwar schon 5 mal gelesen, was denn die Aufgabe des ViewModels ist, aber iwie hakt es da ein wenig.

Databindings: Ja, sollte ich benutzen.
Ich erkenne nur nicht wirklich den Vorteil der Bindings, vermutlich weil ich damit noch nicht gearbeitet habe :freaky::
Die Daten lasse ich mir ja auch automatisch einpflegen und nicht manuell.
Zur Zeit speichere ich mir die Datenbankabfrage in einem Dictionary und erstelle daraus dann einfach in einer foreach Schleife die GUI. Vermutlich verliere ich da ein wenig Effizienz, aber auch die Databindings dürften nicht viel effizienter als eine foreach Schleife sein.


Morpheus, das Vorgehen ist schon einmal sehr gut. Ich muss mal schaun, wann ich wieder Zeit habe, mich daran zu setzen :)



Könnte mir evtl. jemand von euch sagen, wo ich was einordne beim MVVM Konzept?
Model,
View,
ViewModel.

SQL-Abfrage (Model?), Definition DataTemplate (ViewModel?), Definition TreeView (Checkbox im Header) (..??), Button Handler (Codebehind der View ?), DataContext (Codebehind der View ?), TreeView (View), ..




Gruß
 
Zuletzt bearbeitet:
DataBinding erspart dir einfach viele Schleifen. So wird dein Programm vom Code her ärmer und Korrekter (ist Ansichtssache). Korrektheit bezieht sich auf die mögliche Bugs.

MVVM ist so wie MVC
MV ist in beiden gleich aber statt C hast du VM die deine Controller ersetzen. Anstatt den Code (wie du jetzt machst) in die Events zu verpacken wird dieser in deine ViewModel Klasse wandern. Damit bleib die Implementierung deiner WPF Klasse sauber und ohne jegliche Logik (weil dieser Dort nix zu suchen hat).
Kurz erklärt ist der Ansatz der View Model folgendes: Deine VM ist Selbstorganisierend. Du bist komplett unabhängig von der Umgebung. Die Umgebung ist aber umgekehrt Abhängig vom Output deiner View Model.
 
Zurück
Oben