Entwickler-Ecke

WinForms - Custom ComboBox erstellen


Kasko - Mo 20.08.18 21:50
Titel: Custom ComboBox erstellen
Hallo zusammen,

da ich mit den Möglichkeiten zur Anpassung einer ComboBox nicht zufrieden bin, möchte ich mir kurzerhand eine eigene ComboBox erstellen. Zuerst einmal die Gründe warum ich nicht zufrieden bin:

Es geht vor allem um das Design, welches in diesem standard Control-Grau vorliegt, und welches man nur bedingt anpassen kann. Es gibt zwar die Möglichkeit Eigenschaften, wie die backcolor zu setzen, aber auch das zeigt nur in Maßen die gewünschten Resultate. Eine weitere Möglichkeit ist es die DrawItem-Methode bzw. das DrawItem-Event zu verwenden aber dies hat auch einige Lücken. Es gibt demnach 3 Dinge die mich stören:

1. Die Eigenschaft backcolor verändert lediglich den Hintergrund des DropDowns und des TextFeldes bzw. des Feldes, in dem das aktuelle Item angezeigt wird (nur im Modus DropDown ein TextFeld mit AutoComplete). Unverändert bleiben sowohl die Border als auch der "Foldout-Pfeil". Diese behalten ihre Control-Color, was bei einigen UI-Designs unerträglich ist.

2. Die Farbe der Textmarkierung lässt sich nicht verändern. Bei einer Hintergrundfarbe von beispielsweise R:45 G:45 B:45, also einem dunklen Grau, sieht das Blau zum Beispiel sehr, sehr unpassend aus.

3. Um das Dropdown-Element wird eine bläuliche Border gezogen, welche im normalen Control-Design vielleicht ganz schön aussieht, aber mit anderen Farben sticht sie erstens zu sehr heraus und zweitens sieht sie einfach blöd aus.

Dies sind die 3 Hauptgründe, warum ich eine eigene ComboBox erstellen möchte. Die einzige Sache, die mir schon im theoretischen Ansatz Kopfzerbrechen bereitet ist die Dropdown-List. Diese ist nämlich anscheinend nicht direkt Teil des Steuerelementes. Hier was ich meine:

Normalerweise werden Steuerelemente, wenn sie mit einer Dockeigenschaft versehen werden, welche nicht None lautet, immer neu arrangiert (Location und Size), je nachdem welche Elemente gerade sichtbar sind und welche nicht. Eine ComboBox verhält sich jedoch grundlegend anders. Wenn man den DockStyle auf Top setzt und und ein weiteres beliebiges Element darunter platziert, ebenfalls mit Dock -> Top, und man das Dropdown Element ausfährt, dann wird es über das andere Element drüber gezeichnet, was beweißt, dass es nicht direkt Teil des ComboBox-Formulares ist, da sonst die Höhe hätte verändert werden müssen um das Element sichtbar zu machen und dies hätte das andere Element nach unten geschoben. Mir stellt sich also die Frage, aus was besteht so ein DropDown und wie kann ich das nachbauen?

LG Kasko ;)

PS Wenn jemand Lösungen zu den 3 oben genannten Problemen hat, sind diese auch gerne willkommen.


Ralf Jansen - Mo 20.08.18 22:25

Zitat:
Mir stellt sich also die Frage, aus was besteht so ein DropDown und wie kann ich das nachbauen?


Eine ComboBox besteht nicht aus Einzelteilen die in Winforms schon da sind. Es ist ein Windows Standardcontrol und der Winforms Wrapper darum eine mehr oder weniger leere Hülle. Nun ja leer ist etwas übertrieben aber zumindest bezogen auf das visuelle ziemlich treffend. Die DropDown Liste (wenn du nur das Verhalten/Aussehen beim DropDownStyle DropDown meinst) kannst du dir am ehesten als eigene (nichtmodale) Form vorstellen.

Bedenke bei deinem Rant über das Aussehen der ComboBox das der Look auf allen Windowsversionen mit allen eingestellten Themes halbwegs funktionieren muss. Die Windows Standardcontrols sind so geschrieben um einen applikationsübergreifenden einheitlichen Look zu gewährleisten und nicht um das individuelle Ausleben irgendwelcher Designideen zu erleichtern. Wenn du alle Freiheiten willst bist du bei Winforms falsch. Bei den Standardcontrols optisch irgendetwas auch nur leicht anders machen zu wollen führt üblicherweise dazu alles selbst machen zu müssen.


Kasko - Mo 20.08.18 23:04

Zitat:
Wenn du alle Freiheiten willst bist du bei Winforms falsch.


Was gibt es denn für Alternativen, die mir mehr Spielraum im Punkt UI-Design geben, aber trotzdem noch ungefähr die Strukturierungsmöglichkeiten von Winforms haben?


Christian S. - Mo 20.08.18 23:56

Eine große Flexibilität in Bezug auf das Erstellen von individuellen Benutzeroberflächen bietet im .NET-Bereich die Windows Presentation Foundation (WPF): https://docs.microsoft.com/de-de/visualstudio/designers/getting-started-with-wpf

Die arbeitet nicht mit den klassichen Windows-Controls und Du kannst - bei entsprechender Einarbeitung in die Materie - alles selber styles und designen.

Der Umstieg von WinForms auf WPF ist aber nicht trivial. Da ist zum einen ein neues Framework für UI samt einer neuen, XML-basierten Beschreibungssprache (XAML), zugleich ist WPF aber auch sehr viel stärker darauf ausgelegt, dass man ein Pattern wie MVVM durchzieht und die Benutzerobefläche durch Data Binding mit Leben gefüllt wird.


Th69 - Di 21.08.18 09:36

How to create a custom ComboBox from scratch [https://www.codeproject.com/Articles/30958/How-to-create-a-custom-ComboBox-from-scratch] erstellt eine eigene (WinForms-)ComboBox auf Basis einer TextBox und einer ListBox.


Kirk1701A - Di 21.08.18 19:03

Ich hätte das in einer CSS festgelegt und dann einen Verweis darauf gesetzt, dass es auch funktioniert.


Christian S. - Di 21.08.18 19:08

CSS in WinForms :gruebel:


Kasko - Mi 22.08.18 16:55

Ich bin jetzt dabei das DropDown Menu zu erstellen. Ich nutze dafür ein ToolStripDropDown. Es gibt nur ein Problem. Sowohl die Hintergrundfarbe des DropDown als auch die Hintergrundfarben der Label die die Item repräsentieren lassen sich nicht setzen. Hat jemand dafür eine Lösung? Im Internet finde ich Lösungen zu ToolStrips in denen ToolStripRenderer verwendet werden. Ich weiß aber nicht genau wie ich sie einsetzen soll um das zu erreichen was ich möchte.


Ralf Jansen - Mi 22.08.18 20:04

Ich vermute mal du meinst die einzelnen ListBox Items die in deiner ListBox stecken die du als DropDown mißbrauchst? Zumindest macht das der verlinkte Beispielcode.

In dem Fall musst du die ListBox auf selbstzeichnen stellen (via der DrawMode Property). Jetzt zeichnet die ListBox ihren Inhalt nicht mehr sondern feuert für dich den DrawItem Event wo du dann das Item selbst zeichnen kannst. Die Doku für ListBox.DrawMode [https://msdn.microsoft.com/de-de/library/system.windows.forms.listbox.drawmode(v=vs.110).aspx] hat dafür ein Beispiel. Sei dir aber bewusst das das Beispiel nur ein Beispiel ist. Was das zeichnen betrifft ist es natürlich unvollständig. Es zeigt nur wie man irgendetwas in dem Event zeichnet aber nicht was man alles noch berücksichtigen muss oder sollte. Z.B. sollten das markierte Item anders aussehen als die anderen. Wenn man mit der Maus über einem Item hovert soll es möglicherweise auch anders aussehen etc.

Übrigens Controls haben natürlich auch bestimmte Verhalten nicht nur aussehen. Die Wahrscheinlichkeit das eine ListBox in einem ToolStrip sich genauso verhält wie eine ComboBox ist eher gering. Ich vermute mal schwer das man so die Tastaturbedienung zerlegt bzw. sich das anders verhält und sich eine solche ~Combobox~ regelmäßig zu den falschen Zeitpunkten schließt oder ähnliches. Will sagen deine Combobox mag nachher schöner aussehen sie wird sich aber vermutlich in vielen Bereichen anders verhalten um nicht zu sagen falsch.


Kasko - Mi 22.08.18 22:58

Ich verwende keine ListBox. Ich verwende einfach nur ein ToolStripDropDown, dem ich dann mehrere ToolStripLabel hinzufüge. Es gibt aber mehrere Probleme aber zuvor mein momentaner Render-Code:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
using System.Drawing;
using System.Windows.Forms;

namespace CustomControls {
    public class LabelToolStripRenderer : ToolStripRenderer {
        private Color toolStripBorderColor, labeBackColor;

        public LabelToolStripRenderer(Color borderColor, Color labelColor) {
            labeBackColor = labelColor;
            toolStripBorderColor = borderColor;
        }

        protected override void OnRenderLabelBackground(ToolStripItemRenderEventArgs e) {
            SolidBrush brush = new SolidBrush(labeBackColor);
            Rectangle rect = new Rectangle(e.Item.ContentRectangle.X, e.Item.ContentRectangle.Y, e.ToolStrip.Width - 2, e.Item.Height);
            e.Graphics.FillRectangle(brush, e.Item.ContentRectangle);
            brush.Dispose();
        }

        protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) {
            Pen pen = new Pen(toolStripBorderColor, 1);
            e.Graphics.DrawRectangle(pen, e.ToolStrip.Bounds);
        }
    }
}


Hier die Probleme:

1. Es wird nicht immer die gesamte Zeile des Labels mit der Hintergrundfarbe gefüllt. Der Grund ist klar. Die AutoSize Eigenschaft ist auf true gesetzt und deshalb reicht das Label halt nicht immer bis zum Ende. Das Problem ist, da ich möchte dass immer die gesamte Zeile ausgefüllt wird, muss ich AutoSize auf false setzen. Dabei treten aber einige Probleme auf. Zum einen schrumpft das DropDown auf eine Breite von nicht mehr als 10, egal welchen Wert ich für Width einsetze. Zudem liegen die Labels nicht mehr an der linken Seite sondern sind zentriert auf ihrer Zeile.

2. Kurz und knapp. Die OnRenderToolStripBorder Methode zeichnet die Border nicht.

3. Ebenfalls kurz und knapp. Ich finde keine Methode, mit der ich das Zeichnen des Hintergrundes des DropDowns anpassen kann. Die Eigenschaft BackColor funktioniert auch nicht.

Edit:

Nur noch das 1. Problem.


Ralf Jansen - Do 23.08.18 19:51

Da sind wir aber jetzt aber ziemlich weit weg von einer ComboBox.

Wenn sich dein ToolStripRenderer komisch verhält hilft dir vielleicht anzusehen wie Microsoft das gelöst hat? Z.b. im ToolStripProfessionalRenderer [https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/ToolStripProfessionalRenderer.cs].

OnRenderLabelBackground sieht zwar unauffällig aus (nicht prinzipiell anders als dein Code) aber vielleicht siehst du ja ein Detail das dir entgangen ist.