Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 18 Min.

UI-Controls

Visuelle Steuerelemente dienen der Eingabe und Anzeige von Daten sowie der Interaktion mit den Usern. Die Wahl ansprechender Controls maximiert die User Experience.
© dotnetpro
User-Interface-Controls (UI-Controls) sind visuelle Elemente oder Widgets, die auf der Benutzeroberfläche angezeigt werden. Sie ermöglichen es den Benutzern, mit der Anwendung zu interagieren. UI-Controls können verschiedene Formen annehmen, wie zum Beispiel Buttons, Schieberegler, Textfelder, Drop-down-Listen, Checkboxen oder Radio-Buttons. Neben der Anzeige von Informationen oder der Datenerfassung dienen sie dazu, die Interaktion zwischen den Benutzern und der Anwendung zu erleichtern. Die Wahl passender UI-Controls soll zu einer intuitiven und besser bedienbaren Oberfläche führen.Neben den funktionalen Aspekten sind die UI-Controls auch für das Design der App verantwortlich. In diesem Teil der Artikelserie zum Thema „Design moderner Windows-Apps mit dem Grafikframework WinUI 3“ dreht sich alles um die UI-Controls für WinUI 3. Wir werden uns ansehen, welche UI-Controls (Steuerelemente) das Grafikframework Win­UI 3 von Haus aus bietet. Es geht unter anderem darum, sich mit wichtigen und auch neuen UI-Controls vertraut zu machen und vielleicht auch daraus ganz neue Ideen für die Dateneingabe, die Anzeige von Informationen und die Interaktion zu entwickeln und umzusetzen. Ebenso werden wir feststellen, dass noch Steuerelemente für den ein oder anderen Zweck im Basisframework fehlen. Einige davon werden hoffentlich noch mit künftigen Updates nachgerüstet. Andere Lücken in der Control-Gallery können wir gegebenenfalls über das Angebot von Drittanbietern schließen.Microsoft stellt – Stand heute – mehr als 45 UI-Controls bereit. Diese sind ein Teil des Fluent-Design-Systems und eignen sich zur Gestaltung ansprechender Oberflächen. Zusätzliche Steuerelemente kommen von Drittanbietern, etwa Telerik, Syncfusion, DevExpress, ComponentOne und ActiPro.

Problemlage

Im Kern geht es darum, festzulegen, welches UI-Control in der jeweiligen Situation am besten geeignet ist. Einflussfaktoren sind die Funktionalität, die User Experience (UX) und das Design. Alle drei Dimensionen sind zu berücksichtigen und beeinflussen sich gegenseitig. Ein Beispiel: Es gibt nicht nur eine Form (Art) eines Buttons, sondern zahlreiche unterschiedliche Ausprägungen von Schaltflächen. Ein Blick in die Dokumentation klärt uns hier auf. Das Grafikframework Win­UI 3 stellt folgende Button-Varianten zur Auswahl (Bild 1):
Reiche Auswahl an Buttons in WinUI 3 (Bild 1) © Autor
  • Button
  • DropDownButton
  • HyperlinkButton
  • RepeatButton
  • SplitButton
  • ToggleSplitButton
Diese Auswahl wirkt zunächst verlockend, hat man doch die Wahl und kann aus einem reichen Angebot schöpfen. Es ist aber nicht so gedacht, die UI-Controls situativ nach Wahl zu verwenden. Damit die Anwender ein einheitliches Erscheinungsbild und eine durchgängige UX auf allen Windows-Anwendungen wiederfinden, gibt es Richtlinien und Hinweise, welches Control in welcher Situation gut geeignet ist beziehungsweise wie man es eher nicht einsetzen sollte.Bei anderen UI-Controls stellt sich die Frage noch aus ­einer weiteren Perspektive. Auch dazu ein Beispiel: In welcher Form kann man den Fortschritt eines Prozesses anzeigen? Hier gibt es beispielsweise folgende Möglichkeiten:
  • Textfeld mit Prozentanzeige,
  • Progress Bar,
  • ProgessRing mit farbiger Hervorhebung.
Welche Variante man wählt, kann von unterschiedlichen Faktoren abhängen. Wie viel Platz habe ich auf der Oberfläche? Wie exakt muss der Wert ablesbar sein? Welche Information soll transportiert werden? Vor einer Entscheidung für ein konkretes Control sind die Beweggründe gut abzuwägen.Verschaffen wir uns zunächst einen Überblick über die UI-Controls, die mit WinUI 3 ausgeliefert werden (Bild 2) [1]. Wir bezeichnen diese als Basis-Controls; sie lassen sich in die folgenden Bereiche einteilen:
Das RatingControl als neues UI-Control für WinUI 3 (Bild 3) © Autor
  • Eingabeelemente (Interaktion): Das sind die unterschiedlichen Varianten einer Schaltfläche (Button), das Element zur Farbauswahl (ColorPicker), der Slider oder das RatingControl. Das RatingControl kennen wir beispielsweise sehr häufig aus Webapplikationen, um ein Produkt oder eine Dienstleistung aus Kundensicht zu bewerten. Wir finden es jetzt auch in WinUI 3 und können damit auch ähnliche Sachverhalte in einer grafisch sehr einfach zu interpretierenden Form anzeigen (Bild 3).
UI-Controls für WinUI im Überblick (Bild 2) © Autor
  • Eingabeelemente (Text):Hierunter fallen Steuerelemente, die einen Text oder Zahlenwert durch Eingabe der Benutzer entgegennehmen. Für die Anzeige von schreibgeschütztem Text stehen die Steuerelemente TextBlock und RichTextBlock zur Verfügung. Die Steuerelemente für Text­eingabe und Bearbeitung sind TextBox, RichEditBox, AutoSuggestBox und PasswordBox. Immer wenn Sie Text- und/oder Zahlenwerte erfassen müssen, prüfen Sie, ob es für den konkret zu erfassenden Wert ein „Spezial-Control“ gibt. Beispielsweise ein Textfeld, das auf die Eingabe von Zahlenwerten eingeschränkt ist (NumberBox) oder das die Zeichen während der Eingabe ausblendet, wie man dies in der Regel für das Erfassen von Passwörtern wünscht (PasswordBox). Diese Steuerelemente reduzieren den Aufwand für die Erstellung des Datenerfassungsdialogs und vereinfachen insbesondere die Datenvalidierung.
  • Collections: Hierunter fallen Steuerelemente, die es ermöglichen, mehrere Elemente (Items, Kind-Elemente) anzuzeigen. Die Darstellung der Kind-Elemente kann dabei über Templates angepasst werden. Zu den Collection-Steuerelementen gehören die Controls ListView und TreeView. Das häufig in Business-Anwendungen benötigte DataGrid gibt es in der Basissammlung der Controls im WinUI 3-Framework leider noch nicht. Hier müssen wir auf Controls von Drittanbietern zurückgreifen. Das ist etwas ärgerlich, denn fast jede Unternehmens-App muss Daten in Tabellenform – meist aus Datenbanken stammend – anzeigen. Das Data­Grid ist unserer Meinung nach ein unverzichtbares Steuer­element. Wir kommen in einem späteren Abschnitt in diesem Artikel darauf zurück. Bei den Collections finden wir auch das FlipView-Control, um beispielsweise durch Bilder oder andere Datensammlungen (definiert via DataSource) zu „flippen“. Ein Beispiel sehen Sie in Bild 4.
Das CalendarView-Control zur Arbeit mit Kalenderdaten (XAML-Code und View) (Bild 5) © Autor
  • Datum und Uhrzeit: Hierunter fallen Steuerelemente zur Auswahl und zur Anzeige von Zeit- und Datumsinforma­tionen. Das CalendarView-Control ist dabei das leistungsfähigste Control. Wir können einen oder mehrere Kalendertage auswählen. Ebenso lässt sich die Sprache einstellen und die Kalenderform, zum Beispiel Gregorianischer Kalender, bestimmen (Bild 5). Einfachere Controls aus diesem Bereich sind das DatePicker- und das TimePicker-Control zur Auswahl von Datum und Uhrzeit. Wir werden diese Controls noch später im Beispiel verwenden.
Ein FlipView-Control, um durch eine Bildersammlung zu swipen (Bild 4) © Autor
  • Dialoge und Flyouts: Hier finden wir unter anderem das Steuer­element ContentDialog. Bevor man ein eigenes Dialogfenster gestaltet, kann dieses Control die Lösung sein. Der Inhalt des Meldungsfensters lässt sich individuell mittels XAML definieren. Da das eigene Steuerelement von der Klasse ContentDialog abzuleiten ist, gibt es dann unter anderem die Methode ShowAsync() zur Anzeige des Dialogfelds und die Eigenschaften PrimaryButtonText, SecondaryButtonText und DefaultButton zur Definition und Anzeige der Buttons. Innerhalb der Content-Eigenschaft fügen wir dann den individuellen Inhalt ein. Ein Beispiel zeigen Bild 6 sowie Listing 1 und Listing 2. Beachten Sie: Die Definition des Dialogfelds erfolgt innerhalb der aufrufenden Seite. Ein weiteres Steuerelement aus diesem Bereich ist das Flyout. Ein Flyout ist ein einfach ausblendbarer Container, der beliebige Inhalte anzeigen kann. Flyouts können andere Flyouts oder Kontextmenüs enthalten. Wann sollten Sie ein Flyout einsetzen? Typische Beispiele sind das Erfassen zusätzlicher Informationen, die erforderlich sind, bevor ­eine Aktion abgeschlossen werden kann, und das Anzeigen von Informationen, die nur vorübergehend relevant sind. So könnte man in einer Fotogalerie-App ein Flyout einsetzen und darauf hinweisen, dass eine größere Version des Bildes angezeigt wird, wenn der Benutzer auf eine Miniaturansicht klickt. Ein anderes Beispiel ist der Hinweis auf die Anzeige weiterer Detailinformationen zu einem Objekt, wenn dieses mit der Maus „überfahren“ wird. Das Flyout-Control ist typisch für den neuen Fluent-Design-Ansatz, der auch in WinUI 3 intensiv zum Einsatz kommt. Ein spezielles Flyout ist das Steuerelement TeachingTip. Es ist ein semipersistentes und inhaltsreiches Flyout, das Kontextinformationen bereitstellt. Es wird häufig verwendet, um über wichtige und neue Features zu informieren, welche die Benutzerfreundlichkeit verbessern. Ebenso kann es auf Features aufmerksam machen, die man vielleicht noch nicht kennt. Daher kommt auch die Bezeichnung TeachingTip. Den Inhalt ­eines TeachingTip kann man unterschiedlich umfangreich gestalten, beispielsweise lediglich mit Textelementen, ergänzend mit Bildern oder mit interaktiven Schaltflächen. Ebenso kann man einen solchen Tipp zielgerichtet (zugehörig zu einem anderen Element) oder frei anordnen. Grundsätzlich gilt: Man sollte TeachingTips nicht zu häufig anzeigen. Sie werden am ehesten beachtet, wenn sie während langer Sitzungen oder über mehrere Sitzungen hinweg gestaffelt angezeigt werden. Ebenso sollte ihre Gestaltung kompakt und gut verständlich sein.
Ein Dialogfeld, das auf der Klasse ContentDialog beruht (Bild 6) © Autor
Listing 1: Definition eines eigenen Meldungsfensters per Klasse ContentDialog (XAML-Code)
<span class="php"><span class="hljs-meta"><?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"utf-8"</span> <span class="hljs-meta">?></span></span><br/><span class="hljs-tag"><<span class="hljs-name">Window</span></span><br/><span class="hljs-tag">  <span class="hljs-attr">...</span>></span><br/>  <span class="hljs-tag"><<span class="hljs-name">StackPanel</span></span><br/><span class="hljs-tag">    <span class="hljs-attr">HorizontalAlignment</span>=<span class="hljs-string">"Center"</span>     <span class="hljs-attr">VerticalAlignment</span>=<span class="hljs-string">"Center"</span>       <span class="hljs-attr">Orientation</span>=<span class="hljs-string">"Horizontal"</span>></span><br/>    <span class="hljs-tag"><<span class="hljs-name">Button</span> <span class="hljs-attr">Click</span>=<span class="hljs-string">"Button_Click"</span> <span class="hljs-attr">Content</span>=<span class="hljs-string">"Dialog..."</span>       /></span><br/>    <span class="hljs-tag"><<span class="hljs-name">ContentDialog</span></span><br/><span class="hljs-tag">      <span class="hljs-attr">x:Name</span>=<span class="hljs-string">"termsOfUseContentDialog"</span></span><br/><span class="hljs-tag">      <span class="hljs-attr">CloseButtonText</span>=<span class="hljs-string">"Cancel"</span>         <span class="hljs-attr">IsPrimaryButtonEnabled</span>=<span class="hljs-string">"False"</span>         <span class="hljs-attr">PrimaryButtonText</span>=<span class="hljs-string">"Accept"</span>></span><br/>      <span class="hljs-tag"><<span class="hljs-name">ContentDialog.TitleTemplate</span>></span><br/>        <span class="hljs-tag"><<span class="hljs-name">DataTemplate</span>></span><br/>          <span class="hljs-tag"><<span class="hljs-name">StackPanel</span> <span class="hljs-attr">Orientation</span>=<span class="hljs-string">"Horizontal"</span>></span><br/>            <span class="hljs-tag"><<span class="hljs-name">TextBlock</span> <span class="hljs-attr">Text</span>=<span class="hljs-string">"Terms of use"</span> /></span><br/>          <span class="hljs-tag"></<span class="hljs-name">StackPanel</span>></span><br/>        <span class="hljs-tag"></<span class="hljs-name">DataTemplate</span>></span><br/>      <span class="hljs-tag"></<span class="hljs-name">ContentDialog.TitleTemplate</span>></span><br/>      <span class="hljs-tag"><<span class="hljs-name">StackPanel</span>></span><br/>        <span class="hljs-tag"><<span class="hljs-name">TextBlock</span> <span class="hljs-attr">TextWrapping</span>=<span class="hljs-string">"WrapWholeWords"</span>></span><br/>          <span class="hljs-tag"><<span class="hljs-name">Run</span>></span><br/>            Lorem ipsum dolor sit amet, consectetuer               adipiscing elit. Maecenas porttitor               congue massa. Fusce posuere, magna sed               pulvinar ultricies, purus lectus<br/>          <span class="hljs-tag"></<span class="hljs-name">Run</span>></span><span class="hljs-tag"><<span class="hljs-name">LineBreak</span> /></span><br/>          <span class="hljs-tag"><<span class="hljs-name">Run</span>></span>Nunc viverra imperdiet enim. Fusce est.             Vivamus a tellus.<span class="hljs-tag"></<span class="hljs-name">Run</span>></span><span class="hljs-tag"><<span class="hljs-name">LineBreak</span> /></span><br/>          <span class="hljs-tag"><<span class="hljs-name">Run</span>></span><br/>            Pellentesque habitant morbi tristique               senectus et netus et malesuada fames<br/>              ac turpis egestas. Proin pharetra               nonummy pede. Mauris et orci.<br/>          <span class="hljs-tag"></<span class="hljs-name">Run</span>></span><span class="hljs-tag"><<span class="hljs-name">LineBreak</span> /></span><br/>          <span class="hljs-tag"><<span class="hljs-name">Run</span>></span><br/>            Suspendisse dui purus, scelerisque at,               vulputate vitae, pretium mattis, nunc.<br/>          <span class="hljs-tag"></<span class="hljs-name">Run</span>></span><br/>        <span class="hljs-tag"></<span class="hljs-name">TextBlock</span>></span><br/>        <span class="hljs-tag"><<span class="hljs-name">CheckBox</span></span><br/><span class="hljs-tag">          <span class="hljs-attr">Margin</span>=<span class="hljs-string">"0,20"</span> <span class="hljs-attr">x:Name</span>=<span class="hljs-string">"ConfirmAgeCheckBox"</span></span><br/><span class="hljs-tag">          <span class="hljs-attr">Content</span>=<span class="hljs-string">"Ich bin über 18 Jahre."</span> /></span><br/>      <span class="hljs-tag"></<span class="hljs-name">StackPanel</span>></span><br/>    <span class="hljs-tag"></<span class="hljs-name">ContentDialog</span>></span><br/>  <span class="hljs-tag"></<span class="hljs-name">StackPanel</span>></span><br/><span class="hljs-tag"></<span class="hljs-name">Window</span>></span> 
Listing 2: Definition eines eigenen Meldungsfensters per Klasse ContentDialog (C#-Code)
private <span class="hljs-keyword">async</span> <span class="hljs-keyword">void</span> Button_Click(object sender,     RoutedEventArgs e)<br/>{<br/>  ContentDialogResult result =     <span class="hljs-keyword">await</span> termsOfUseContentDialog.ShowAsync();<br/>  <span class="hljs-keyword">if</span> (result == ContentDialogResult.Primary)<br/>  {<br/>    ...    <br/>  }<br/>  <span class="hljs-keyword">else</span><br/>  {<br/>    ...     <br/>  }<br/>}<br/>      <<span class="hljs-regexp">/StackPanel></span><br/><span class="hljs-regexp">    </</span>ContentDialog><br/>  <span class="xml"><span class="hljs-tag"></<span class="hljs-name">StackPanel</span>></span></span><br/><span class="xml"><span class="hljs-tag"></<span class="hljs-name">Window</span>></span></span> 
  • Medienelemente: Hier haben wir beispielsweise das UI-Control AnimatedVisualPlayer. Mithilfe dieses Steuerelements kann man nativ gerenderte Adobe-After-Effects-Animationen in der Anwendung präsentieren. Genutzt wird die sogenannte Lottie-Familie für Animationen, die auch auf Android, iOS und das Web ausgerichtet sind. Lottie vereinfacht den Design-to-Code-Workflow, um ansprechende, interaktive Vektoranimationen in Windows-Anwendungen zu integrieren. Lottie-Windows verwendet das Windows.UI.Composition-API, um Animationen mit 60 fps und mit auflösungsunabhängigen Vektorgrafiken bereitzustellen. Auf diese Weise können unsere Apps – durchaus auch Business-Apps – um reichhaltige grafische Elemente (Animationen) angereichert werden. Wer sagt denn, dass Business-Apps langweilig sein müssen und den Nutzern kein Lächeln ins Gesicht zaubern dürfen? Hier muss jedoch auf zwei Aspekte hingewiesen werden. Erstens müssen die Animationen sparsam und gezielt eingesetzt werden. Zum Zweiten erfordert das Erstellen von eigenen Anima­tionen Kenntnisse aus den Bereichen Grafik und Design, das heißt, ein Profi ist gefragt. Weitere UI-Controls aus dem Bereich der Medienelemente sind das Image-Steuerelement (Bild-Anzeige), das MediaPlayer-Element, das PersonPicture-Control, das SoundControl und das WebView2-Steuerelement. Das PersonPicture-Control ermöglicht die Anzeige von Daten eines Benutzers in Form von Bild und Initialen beziehungsweise des Namens in grafisch aufbereiteter Form. Dieses Element dient uns beispielsweise dazu, Visitenkarten von Anwendern einzublenden. Auch zu diesem Steuerelement haben wir ein Beispiel vorbereitet (Bild 7).
Beispiel zum PersonPicture-Control (Bild 7) © Autor
  • Menüs und Toolbars: Auch in WinUI-3-Apps können wir ein klassisches Anwendungsmenü einfügen. Dieses lässt sich mit Untermenüs anreichern und auch um Shortcuts für ­eine Bedienung mittels Tastatur ergänzen. Ebenso stehen Con­trols für die Gestaltung einer Toolbar zur Verfügung. Das sind die Controls AppBarButton, AppBarToggleButton und AppBarSeparator. Wenn Sie eine WPF- oder eine WinForms-Anwendung migrieren, dann vermissen Sie vielleicht das unter Office übliche Ribbon-Menü. Das gibt es in der Basisbibliothek von WinUI 3 nicht. Wir werden aber zum Beispiel beim Anbieter Syncfusion fündig. Dieser stellt ein entsprechendes Element bereit [2]. Mithilfe dieses Controls können wir das Menü einer Anwendung nach dem Vorbild von Microsoft Office aufbauen. Ein Menü lässt sich alternativ auch als Kontextmenü, zugeordnet zu einem anderen UI-Element, definieren. Beachten Sie jedoch: Menüs und Kontextmenüs sind Controls, welche die Kommandos von den eigentlichen App-Inhalten trennen. Der Trend geht jedoch in die Richtung, die Interaktion unmittelbar über den Content vorzunehmen. Mit anderen Worten: Es ist intuitiver, auf ein Listenelement zu klicken oder dieses zu berühren, als ein Menüelement auszuwählen. Prüfen Sie daher, ob der Einsatz eines Menüs noch zeitgemäß ist oder ob es eine moderne Alternative beim Bedienkonzept gibt.
  • Navigation: Die Steuerelemente aus diesem Bereich legen die Navigation der App fest. Zur Auswahl stehen die NavigationView, das Pivot-Element und die TabView. Wir haben die grundsätzlichen Varianten zur Navigation im ersten Teil der Serie thematisiert. Damit sich die Nutzer in komplexen App-Strukturen besser zurechtfinden, kann man ein BreadcrumbBar-Control einsetzen. Dieses dient der kombinierten Anzeige und der schnellen Navigation innerhalb einer App (Bild 8).
Navigation mittels BreadcrumbBar (Bild 8) © Autor
  • Status und Informationen: Nutzer sind im Regelfall ungeduldig, das heißt, auf eine Aktion beziehungsweise eine Reaktion hin erwarten sie, dass das System auch unmittelbar reagiert. Mit anderen Worten: Die App sollte stets reaktionsfreudig sein, das heißt das User Interface „flüssig“ und nicht eingefroren daherkommen. Einige Aktionen erfordern jedoch eine längere System- beziehungsweise Rechenzeit. Hier unterscheiden wir mehrere mögliche Zustände. Eine bestimmte Aktion wird durch die Anwender angestoßen und das System führt diesen Vorgang ohne weiteres Zutun von Anfang bis zum Ende alleine durch. Ein typisches Beispiel ist der Download einer größeren Datei. Dieser Vorgang erfolgt mehr oder weniger schnell, je nachdem, wie groß die Datei und wie schnell die Internetverbindung ist. Nach dem Abschluss des Vorgangs sollte eine Information an die Benutzer erfolgen und der darauffolgende Vorgang kann gestartet werden. Über den laufenden Fortschritt des Downloads sollte informiert werden. Hier bietet sich beispielsweise ein ProgressBar-Steuerelement an, das den Fortschritt in Prozent vom Gesamtwert darstellt. Eine alternative Darstellung erfolgt mittels ProgressRing. Ist der aktuelle Fortschritt bekannt beziehungsweise das Ende des Vorgangs kalkulierbar, dann nutzen wir beispielsweise einen ProgressRing, dessen Kreis sich stetig füllt. Es handelt sich um eine sogenannte bestimmte Darstellung. Ist die Dauer des Vorgangs unbekannt oder können wir den Fortschritt nicht stetig messen, dann nutzen wir einen ProgressRing, in dem ein Teilring stetig durchläuft. Wir haben es mit einer unbestimmten Darstellung zu tun. Auf diese Weise signalisieren wir, dass das System noch arbeitet. Ein Beispiel ist die Anmeldung des Clients am Server. Man hat im Regelfall keine genaueren Informationen, wie lange dieser Vorgang dauert, und man kann auch keinen messbaren Fortschritt anzeigen. Bei einer unbestimmten Darstellung (durchlaufender Teilring) müssen wir jedoch darauf achten, dass wir gegebenenfalls das Erreichen von Meilensteinen des Prozesses – auch wenn wir keinen exakten Fortschritt berechnen können – über eine Textnachricht als Zwischenfeedback an die Benutzer geben. Es darf nicht der Eindruck entstehen, dass sich das System in einer Endlosschleife befindet oder sogar abgestürzt ist. Gelegentlich kann es auch notwendig sein, den Anwendern aktiv eine Rückmeldung zu geben, wie viele Schritte insgesamt notwendig sind, bis ein umfassenderer Vorgang abgeschlossen ist. Das kann beispielsweise beim Ausfüllen von mehreren umfassenden Dialogfeldern (Assistent zur Datenerfassung) der Fall sein. Auch dazu kann ein ProgressBar-Control verwendet werden, und bei beispielsweise fünf Schritten können wir den Fortschrittsbalken mit der Erledigung eines jeden Schritts um weitere 20 Prozent auffüllen. Weitere Elemente aus dem Bereich Status und Information sind die Steuerelemente QuickInfo, InfoBar und Infobadge. Ein QuickInfo ist ein Pop-up, das zusätzliche Informationen zu einem anderen Steuerelement oder Objekt enthält. QuickInfos werden automatisch angezeigt, wenn der Benutzer den Fokus auf das zugehörige Steuerelement verschiebt, drückt und hält oder den Zeiger über das zugeordnete Steuerelement bewegt. Die Information wird ausgeblendet, wenn der Benutzer den Fokus wechselt, das Drücken beendet oder die Mauszeiger über das zugeordnete Steuerelement entfernt. Verwenden Sie QuickInfos sparsam, denn diese stellen eine Unterbrechung dar. Die InfoBar können Sie einsetzen, um Informationen weiterzugeben, ohne dabei jedoch den Arbeitsfluss zu unterbrechen oder aufdringlich zu wirken. Beispiele für den Einsatz sind Meldungen wie „Die Internetverbindung wurde getrennt“ und „Für eine Installation des Updates ist ein Neustart der App notwendig“. Über Symbole kann man auf den Schweregrad der Meldung („Fehler“, „Warnung“, „Erfolg“ und „Standardwert“) hinweisen und damit signalisieren, wie dringlich ein Eingreifen ist. Ein Infobadge ist eine weitere Möglichkeit, auf bestimmte Ereignisse in der App hinzuweisen. Bekannt ist diese Form der Information etwa in Form eines Symbols, in dem auf eine bestimmte Anzahl von neuen Nachrichten in der App verwiesen wird. Wir kennen dieses Element aus den mit ­Informationen aufgeladenen Icons einer mobilen App. Es steht jetzt auch in Desktop-Anwendungen zur Verfügung.
  • Scrolling: Wenn mehr Inhalte anzuzeigen sind, als in einen Bereich passen, verwenden Sie das Control ScrollViewer. Dabei sollte man eher einen vertikalen statt eines horizontalen Bildlaufs einsetzen. Es ist zu beachten, dass die Bildlaufleiste selbst Platz verbraucht, im Standardfall beträgt deren Breite 16 Pixel. Wir müssen darauf achten, dass sich in diesem Bereich keine Inhalte befinden. Dieser Aspekt ist insbesondere zu beachten, wenn die Bildlaufleiste nicht stetig, sondern situativ eingeblendet wird. Über eine Abstandsangabe des Contents (Eigenschaft Margin) erreichen wir, dass die Bildlaufleiste keine Inhalte überdeckt.
Wir haben soeben wichtige Steuerelemente vorgestellt, die uns zur Gestaltung von Apps mit dem Grafikframework Win­UI 3 zur Verfügung stehen. Sie haben gesehen, dass es die klassischen und seit vielen Jahren existierenden Elemente wie den Button oder Texteingabe-Elemente auch hier gibt. Dennoch ist die Auswahl an UI-Controls heute viel umfassender. Suchen Sie nach modernen Controls, welche die geplante User Experience umsetzen und Ihrer App ein frisches Design verleihen. Die App WinUI 3 Gallery zeigt Ihnen die mögliche Verwendung vieler Controls. Die App enthält auch kurze Quellcodeausschnitte für die sofortige Verwendung in ­einer eigenen Anwendung. Den Sourcecode können wir oft mittels Copy-and-paste und minimalen Anpassungen übernehmen. Ebenso finden sich für das UI-Control Verweise auf die Dokumentation und die Designrichtlinien (Bild 9).
Die App WinUI 3 Gallery (Bild 9) © Autor
Konfiguriert und angepasst werden die Steuerelemente beispielsweise durch die Verwendung von Animationen oder der Definition von Styles. Fragen zum Design werden wir im kommenden Teil der Artikelserie behandeln.Jetzt werfen wir einen Blick auf Animationen zur Unterstützung der Navigation. In einer App sind fließende Bewegungen wichtig. Sie geben ein angepasstes Feedback basierend auf dem Verhalten der Benutzer und lassen die Oberfläche lebendig wirken. Durch fließende Bewegungen entsteht eine emotionale Verbindung zwischen den Nutzern und dem User Interface. Ausgangspunkt sind die natürlichen Bewegungen, welche die Benutzer aus der realen physischen Welt kennen und die man für ausgewählte Elemente in der App nachahmt. Ein Beispiel: Die Geschwindigkeit des Scrollens der Elemente in einer Liste entspricht der Bewegung des Fingers in Richtung und Geschwindigkeit auf der Oberfläche des Bildschirms. Die Toucheingabe ist daher eine direkte Interaktion zwischen Mensch und App. Daher beeinflusst das Objekt, mit dem die Anwender interagieren, die betreffenden Objekte. Ein weiteres Beispiel für eine sinnvolle Animation sind Seitenübergänge. Diese Form von Animationen vermittelt einen Hinweis darauf, wie Seiten untereinander zusammenhängen.Ebenso kann durch Bewegung die Aufmerksamkeit der Anwender bewusst auf einen bestimmten Punkt ausgerichtet werden. Auch bei der Auswahl eines Objekts aus einer Übersichtsdarstellung (Liste, Tabelle) zu einer Detaildarstellung eines Objekts können Animationen sinnvoll verwendet werden. Das betrifft Apps mit einer Master-Detail-Darstellung. Das WinUI-3-Grafikinterface bietet vorbereitete Animationen in Form von Klassen, die man situativ in der App zur ­Realisierung von Bewegungen einsetzen kann. Hinweise zu diesem Thema finden Sie unter [3].

Weitere Controls

Wir haben die Controls von Microsoft für das WinUI-3-Framework vorgestellt. Wir können mit deren Hilfe einen Großteil der Anforderungen an eine Business-App realisieren. ­Dennoch wird es – beispielsweise bei einer Migration einer WinForms- oder WPF-Anwendung – die eine oder andere „Lücke“ bei der Verfügbarkeit von UI-Controls geben. Diese Lücken können gegebenenfalls durch die Nutzung von ­Steuerelementen, die von Drittanbietern erstellt wurden, geschlossen werden. UI-Controls für WinUI 3 gibt es beispielsweise von Syncfusion [2], DevExpress [4], Telerik [5] und ­GrapeCity [6].Ebenso findet sich das ein oder andere UI-Control, das von der Community zur Verfügung gestellt wird. Die Steuerelemente, die über das Community Toolkit [7] bereitgestellt werden, wurden ursprünglich für die UWP (Universal Windows Platform), also für WinUI 2.x entwickelt. Einem Blogpost, den man ebenfalls auf der Webseite des Toolkits findet, ist der Hinweis zu entnehmen, dass diese Controls mit WinUI 3 funktionieren sollten. Bei einigen Controls findet man auch weitere Hinweise für eine Migration von WinUI 2.x zu WinUI 3. Das bedeutet: Wenn man diese Controls einsetzen möchte, sollte man deren Eignung vorab evaluieren.Dieser Hinweis gilt immer dann, wenn wir komplexere oder weniger bekannte UI-Controls einsetzen möchten. Probieren Sie diese beispielsweise in einer „Wegwerf-App“ aus. Ein Tipp aus der Praxis: Um eine Seite für eine WinUI-3-App zu gestalten, braucht man nicht den gesamten Overhead der App. Kompilieren, Neustart, Änderungen, Hot-Reload, erneute Anpassungen und so weiter – all das geht schneller, wenn wir nur eine simple App mit einer Seite haben. Wir können den XAML-Code erstellen, verifizieren, anpassen und diesen dann später mittels Copy-and-paste in die Produktions-App übernehmen.

Keine Business-App ohne DataGrid

Zugegeben, diese Überschrift ist etwas übertrieben formuliert. Dennoch dürfte gelten: Ein DataGrid ist Bestandteil von sehr vielen Unternehmens-Applikationen. Eine tabellenartige Darstellung ermöglicht die Präsentation, die Änderung und auch das Hinzufügen von neuen Daten. Man kann die Daten auch sortieren und filtern oder nach bestimmten Informationen im Datenbestand suchen.Umso ärgerlicher ist es, dass zum Zeitpunkt der Erstellung dieses Artikels noch kein exklusives DataGrid unter den Basis-Controls von WinUI 3 zu finden ist. In der App WinUI 3 Gallery gibt es zwar den Eintrag DataGrid, dort ist jedoch im Moment lediglich ein Screenshot abgebildet. Was soll das? Der Verweis führt zu einem Beispiel mit einem DataGrid. Das referenzierte DataGrid-Control stammt aus dem Windows Community Toolkit, das ursprünglich für WinUI 2 (UWP) konzipiert und programmiert wurde. WinUI 2 und WinUI 3 basieren zwar auf dem gleichen Ansatz, was bedeutet, sie sind verwandt, aber sie sind nicht komplett kompatibel.In dem Blogbeitrag, den Sie unter [8] finden, ist beschrieben, wie man das Data­Grid-Beispiel von WinUI 2 nach ­WinUI 3 migriert. Der ­Autor dieses Blogs kommt nach seinem Test zu folgendem ­Ergebnis: „Unserer Meinung nach hat das DataGrid aus dem Community Toolkit auf WinUI 3 die Tests bestanden und ist bereit für eine Verwendung. Einige der Funktionen – wie ­Filtern und Gruppieren – erfordern jedoch einen höheren Entwicklungsaufwand als bei Komponenten von Drittanbietern.“Uns überzeugt dieser Workaround nur bedingt. Datenzugriffe und deren Repräsentation sind meist aufwendig und potenzielle Fehlerquellen in einer Anwendung. An dieser Stelle mit einem nicht stabilen DataGrid zu arbeiten ist ein Risiko. In diesem Fall raten wir zur Nutzung eines Controls eines Drittanbieters, beispielsweise des DataGrid von Syncfusion [9]. Dieses bietet folgende Funktionen:
  • Optimiert für die Anzeige von großen Datenbeständen; die Dokumentation spricht von Millionen von Datensätzen.
  • Unterstützt das Konzept der Datenbindung mit verschiedenen Datenquellen.
  • Unterschiedliche Datenarten lassen sich in den Zellen (Spalten) darstellen.
  • Datenspalten können gebunden, aber auch nicht gebunden werden.
  • Sortieren der Daten über einen Klick auf die Spaltenüberschrift möglich.
  • Möglichkeit der Gruppierung der Daten.
  • Daten können gefiltert werden.
  • Validierung der Daten bei der Erfassung.
  • Anpassung des Styles von Zellen und Zeilen.
Die Funktionsweise des Controls ist an vielen Stellen an die der Tabellenkalkulation Excel angelehnt. Die Dokumenta­tion des Herstellers ist vorbildlich und zeigt in umfassender Schrittfolge die Einbindung des Controls in die App und die Verwendung der einzelnen Funktionen. Alternativen zu diesem DataGrid-Control finden wir bei GrapeCity [10], Telerik [11] und auch bei DevExpress [12].

Home Information System

Nun wird es praktisch. Wir werden den Aufbau eines Screens aus mehreren UI-Controls demonstrieren. Wir arbeiten erneut an unserem Home Information System. Leser, welche die ersten beiden Teile verfolgt haben, kennen es schon. Alle anderen finden eine kurze Zusammenfassung im Textkasten Beispielprojekt: Home Information System.

Beispielprojekt: Home Information System

In dieser Artikelserie zeigen wir den gesamten Vorgang von der Idee bis zu der Umsetzung des Designs auf. Dafür haben wir als Beispiel die Entwicklung eines Home Information System ausgesucht.
Heute geht es um die Gestaltung eines Moduls zur Anzeige von Abfahrtsdaten der nächstgelegenen Stadtbus- beziehungsweise Stadtbahnlinie. Starten wir wie gewohnt mit ­einem Prototyp für das zu erstellende User Interface (Bild 10). Es liegt ein grafisch reichhaltiger Entwurf vor.
Prototyp für den Fahrplan-Screen (Bild 10) © Autor
Wir beginnen wiederum mit dem Layout der Seite (siehe Teil 2 dieser Serie [13]). Blicken wir nunmehr genauer auf die User-Interface-Controls:
  • Texteingabefelder für die Auswahl von Start- und Zielort: Hier können wir gegebenenfalls mit einer Autovervollständigung arbeiten.
  • Texteingabefelder für die Auswahl des Datums und der Zeit: ­Eine höhere Benutzerfreundlichkeit erreichen wir, wenn wir mit spezialisierten Datumsauswahl- (DatePicker) und Zeitauswahl-Controls (TimePicker) arbeiten.
  • Button: Standardbutton, gegebenenfalls über Style angepasst zum Auslösen der Suchfunktion.
  • Lauftext für mögliche Störungsmeldungen: Ein sogenanntes Marquee-Element haben wir von Haus aus in WinUI 3 nicht. Hier gibt es mehrere Ansätze, beispielsweise die Einblendung als statischer Text oder eine manuelle Programmierung des horizontalen Scrollings. Eine experimentelle Implementierung eines Marquee-Controls findet man in den Windows Community Toolkit Labs [14].
  • Images für die Schnellauswahl des Zielorts. Hier handelt es sich um Bilder, die wir in eine Gitterstruktur einbinden. Sollten es mehr Symbole sein, als auf den Screen passen, dann muss man ein horizontales Scrolling einbauen.
  • Die Überschrift auf der rechten Seite ist ein statisches Textfeld (TextBlock). Darunter haben wir eine vertikale Liste (ListView). Die Items in der Liste können wir bezüglich des Layouts individuell über die Definition eines ItemsTem­plate gestalten. Ein solches Template kann wiederum aus mehreren TextBlock-Controls et cetera bestehen.
Wir haben einen ersten Entwurf für diese App realisiert. Sie sehen das noch nicht perfekt gestaltete Ergebnis in Bild 11. ­Einen Auszug aus dem Quellcode finden Sie in Listing 3.
Der Fahrplan-Screen in der WinUI-3-App (Bild 11) © Autor
Listing 2: XAML-Code (Auszug) zur Fahrplan-Seite
<span class="xml"><span class="hljs-tag"><<span class="hljs-name">Page</span></span></span><br/><span class="xml"><span class="hljs-tag">    <span class="hljs-attr">x:Class</span>=<span class="hljs-string">"TimeTable.Views.TimeTablePage"</span></span></span><br/><span class="xml"><span class="hljs-tag">    <span class="hljs-attr">...</span>></span></span><br/><span class="xml">  <span class="hljs-tag"><<span class="hljs-name">Grid</span> <span class="hljs-attr">x:Name</span>=<span class="hljs-string">"ContentArea"</span>></span></span><br/><span class="xml">    <span class="hljs-tag"><<span class="hljs-name">Grid.Background</span>></span></span><br/><span class="xml">      ...</span><br/><span class="xml">    <span class="hljs-tag"></<span class="hljs-name">Grid.Background</span>></span></span><br/><span class="xml">    <span class="hljs-tag"><<span class="hljs-name">Grid.Resources</span>></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">Style</span> <span class="hljs-attr">x:Key</span>=<span class="hljs-string">"RoundImage"</span> <span class="hljs-attr">TargetType</span>=<span class="hljs-string">"Rectangle"</span>></span></span><br/><span class="xml"><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">Setter</span> <span class="hljs-attr">Property</span>=<span class="hljs-string">"Width"</span> <span class="hljs-attr">Value</span>=<span class="hljs-string">"150"</span> /></span></span></span><br/><span class="xml"><span class="xml">        ... </span></span><br/><span class="xml"><span class="xml">      </span><span class="hljs-tag"></<span class="hljs-name">Style</span>></span></span><br/><span class="xml">    <span class="hljs-tag"></<span class="hljs-name">Grid.Resources</span>></span></span><br/><span class="xml">    <span class="hljs-tag"><<span class="hljs-name">Grid.RowDefinitions</span>></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">RowDefinition</span> /></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">RowDefinition</span> <span class="hljs-attr">Height</span>=<span class="hljs-string">"50"</span> /></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">RowDefinition</span> <span class="hljs-attr">Height</span>=<span class="hljs-string">"1.2*"</span> /></span></span><br/><span class="xml">    <span class="hljs-tag"></<span class="hljs-name">Grid.RowDefinitions</span>></span></span><br/><span class="xml">    <span class="hljs-tag"><<span class="hljs-name">Grid.ColumnDefinitions</span>></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">ColumnDefinition</span> /></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">ColumnDefinition</span> <span class="hljs-attr">Width</span>=<span class="hljs-string">"300"</span> /></span></span><br/><span class="xml">    <span class="hljs-tag"></<span class="hljs-name">Grid.ColumnDefinitions</span>></span></span><br/><span class="xml">    <span class="hljs-tag"><<span class="hljs-name">Grid</span> <span class="hljs-attr">x:Name</span>=<span class="hljs-string">"Quicklinks"</span> <span class="hljs-attr">Grid.Row</span>=<span class="hljs-string">"2"</span>></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">Grid.RowDefinitions</span>></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">RowDefinition</span> /></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">RowDefinition</span> /></span></span><br/><span class="xml">      <span class="hljs-tag"></<span class="hljs-name">Grid.RowDefinitions</span>></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">Grid.ColumnDefinitions</span>></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">ColumnDefinition</span> /></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">ColumnDefinition</span> /></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">ColumnDefinition</span> /></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">ColumnDefinition</span> /></span></span><br/><span class="xml">      <span class="hljs-tag"></<span class="hljs-name">Grid.ColumnDefinitions</span>></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">Rectangle</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Grid.Row</span>=<span class="hljs-string">"1"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Grid.Column</span>=<span class="hljs-string">"0"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Style</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{StaticResource RoundImage}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">Rectangle.Fill</span>></span></span><br/><span class="xml">          <span class="hljs-tag"><<span class="hljs-name">ImageBrush</span> <span class="hljs-attr">ImageSource</span>=<span class="hljs-string">"/Assets/sport.png"</span> /></span></span><br/><span class="xml">        <span class="hljs-tag"></<span class="hljs-name">Rectangle.Fill</span>></span></span><br/><span class="xml">      <span class="hljs-tag"></<span class="hljs-name">Rectangle</span>></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">Rectangle</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Grid.Row</span>=<span class="hljs-string">"1"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Grid.Column</span>=<span class="hljs-string">"1"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Style</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{StaticResource RoundImage}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">Rectangle.Fill</span>></span></span><br/><span class="xml">          <span class="hljs-tag"><<span class="hljs-name">ImageBrush</span> <span class="hljs-attr">ImageSource</span>=            <span class="hljs-string">"/Assets/bahnhof.png"</span> /></span></span><br/><span class="xml">        <span class="hljs-tag"></<span class="hljs-name">Rectangle.Fill</span>></span></span><br/><span class="xml">      <span class="hljs-tag"></<span class="hljs-name">Rectangle</span>></span></span><br/><span class="xml">       ...</span><br/><span class="xml">    <span class="hljs-tag"></<span class="hljs-name">Grid</span>></span></span><br/><span class="xml">    <span class="hljs-tag"><<span class="hljs-name">StackPanel</span> <span class="hljs-attr">Grid.RowSpan</span>=<span class="hljs-string">"3"</span> <span class="hljs-attr">Grid.Column</span>=<span class="hljs-string">"1"</span>></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">ListView</span> <span class="hljs-attr">ItemsSource</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{x:Bind           ViewModel.Depatures, Mode=TwoWay,           UpdateSourceTrigger=PropertyChanged}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">ListView.HeaderTemplate</span>></span></span><br/><span class="xml">          <span class="hljs-tag"><<span class="hljs-name">DataTemplate</span>></span></span><br/><span class="xml">            <span class="hljs-tag"><<span class="hljs-name">StackPanel</span> <span class="hljs-attr">Margin</span>=<span class="hljs-string">"0,20"</span>                 <span class="hljs-attr">HorizontalAlignment</span>=<span class="hljs-string">"Center"</span>></span></span><br/><span class="xml">              <span class="hljs-tag"><<span class="hljs-name">TextBlock</span></span></span><br/><span class="xml"><span class="hljs-tag">                <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">                <span class="hljs-attr">Text</span>=<span class="hljs-string">"Aktuelle Abfahrten"</span> /></span></span><br/><span class="xml">              <span class="hljs-tag"><<span class="hljs-name">TextBlock</span></span></span><br/><span class="xml"><span class="hljs-tag">                <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">                <span class="hljs-attr">Text</span>=<span class="hljs-string">"Anger, Erfurt"</span> /></span></span><br/><span class="xml">            <span class="hljs-tag"></<span class="hljs-name">StackPanel</span>></span></span><br/><span class="xml">          <span class="hljs-tag"></<span class="hljs-name">DataTemplate</span>></span></span><br/><span class="xml">        <span class="hljs-tag"></<span class="hljs-name">ListView.HeaderTemplate</span>></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">ListView.ItemTemplate</span>></span></span><br/><span class="xml">          <span class="hljs-tag"><<span class="hljs-name">DataTemplate</span> <span class="hljs-attr">x:DataType</span>=<span class="hljs-string">"m:Depature"</span>></span></span><br/><span class="xml">            <span class="hljs-tag"><<span class="hljs-name">StackPanel</span> <span class="hljs-attr">Margin</span>=<span class="hljs-string">"0,5"</span>></span></span><br/><span class="xml">              <span class="hljs-tag"><<span class="hljs-name">StackPanel</span> <span class="hljs-attr">Orientation</span>=<span class="hljs-string">"Horizontal"</span>></span></span><br/><span class="xml">                <span class="hljs-tag"><<span class="hljs-name">TextBlock</span> <span class="hljs-attr">FontSize</span>=<span class="hljs-string">"16"</span>                   <span class="hljs-attr">Text</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{x:Bind Time}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> /></span></span><br/><span class="xml">                <span class="hljs-tag"><<span class="hljs-name">TextBlock</span></span></span><br/><span class="xml"><span class="hljs-tag">                  <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">                  <span class="hljs-attr">Text</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{x:Bind Route}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> /></span></span><br/><span class="xml">                <span class="hljs-tag"><<span class="hljs-name">TextBlock</span></span></span><br/><span class="xml"><span class="hljs-tag">                  <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">                  <span class="hljs-attr">Text</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{x:Bind Destination}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> /></span></span><br/><span class="xml">              <span class="hljs-tag"></<span class="hljs-name">StackPanel</span>></span></span><br/><span class="xml">              <span class="hljs-tag"><<span class="hljs-name">TextBlock</span></span></span><br/><span class="xml"><span class="hljs-tag">                <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">                <span class="hljs-attr">Text</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{x:Bind Delay}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> /></span></span><br/><span class="xml">            <span class="hljs-tag"></<span class="hljs-name">StackPanel</span>></span></span><br/><span class="xml">          <span class="hljs-tag"></<span class="hljs-name">DataTemplate</span>></span></span><br/><span class="xml">        <span class="hljs-tag"></<span class="hljs-name">ListView.ItemTemplate</span>></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">ui:ListViewExtensions.AlternateItemTemplate</span>></span></span><br/><span class="xml">          <span class="hljs-tag"><<span class="hljs-name">DataTemplate</span> <span class="hljs-attr">x:DataType</span>=<span class="hljs-string">"m:Depature"</span>></span></span><br/><span class="xml">            <span class="hljs-tag"><<span class="hljs-name">StackPanel</span> <span class="hljs-attr">Margin</span>=<span class="hljs-string">"0,5"</span>                 <span class="hljs-attr">Background</span>=<span class="hljs-string">"WhiteSmoke"</span>></span></span><br/><span class="xml">              <span class="hljs-tag"><<span class="hljs-name">StackPanel</span> <span class="hljs-attr">Orientation</span>=<span class="hljs-string">"Horizontal"</span>></span></span><br/><span class="xml">                <span class="hljs-tag"><<span class="hljs-name">TextBlock</span> <span class="hljs-attr">FontSize</span>=<span class="hljs-string">"16"</span>                   <span class="hljs-attr">Text</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{x:Bind Time}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> /></span></span><br/><span class="xml">                <span class="hljs-tag"><<span class="hljs-name">TextBlock</span></span></span><br/><span class="xml"><span class="hljs-tag">                  <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">                  <span class="hljs-attr">Text</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{x:Bind Route}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> /></span></span><br/><span class="xml">                <span class="hljs-tag"><<span class="hljs-name">TextBlock</span></span></span><br/><span class="xml"><span class="hljs-tag">                  <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">                  <span class="hljs-attr">Text</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{x:Bind Destination}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> /></span></span><br/><span class="xml">              <span class="hljs-tag"></<span class="hljs-name">StackPanel</span>></span></span><br/><span class="xml">              <span class="hljs-tag"><<span class="hljs-name">TextBlock</span></span></span><br/><span class="xml"><span class="hljs-tag">                <span class="hljs-attr">FontSize</span>=<span class="hljs-string">"14"</span></span></span><br/><span class="xml"><span class="hljs-tag">                <span class="hljs-attr">Foreground</span>=<span class="hljs-string">"red"</span></span></span><br/><span class="xml"><span class="hljs-tag">                <span class="hljs-attr">Text</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{x:Bind Delay}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> /></span></span><br/><span class="xml">            <span class="hljs-tag"></<span class="hljs-name">StackPanel</span>></span></span><br/><span class="xml">          <span class="hljs-tag"></<span class="hljs-name">DataTemplate</span>></span></span><br/><span class="xml">        <span class="hljs-tag"></<span class="hljs-name">ui:ListViewExtensions.AlternateItemTemplate</span>></span></span><br/><span class="xml">      <span class="hljs-tag"></<span class="hljs-name">ListView</span>></span></span><br/><span class="xml">    <span class="hljs-tag"></<span class="hljs-name">StackPanel</span>></span></span><br/><span class="xml">    <span class="hljs-tag"><<span class="hljs-name">Grid</span> <span class="hljs-attr">x:Name</span>=<span class="hljs-string">"InputArea"</span>></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">Grid.RowDefinitions</span>></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">RowDefinition</span> <span class="hljs-attr">Height</span>=<span class="hljs-string">"80"</span> /></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">RowDefinition</span> /></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">RowDefinition</span> /></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">RowDefinition</span> /></span></span><br/><span class="xml">      <span class="hljs-tag"></<span class="hljs-name">Grid.RowDefinitions</span>></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">Grid.ColumnDefinitions</span>></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">ColumnDefinition</span> /></span></span><br/><span class="xml">        <span class="hljs-tag"><<span class="hljs-name">ColumnDefinition</span> /></span></span><br/><span class="xml">      <span class="hljs-tag"></<span class="hljs-name">Grid.ColumnDefinitions</span>></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">Button</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Grid.Row</span>=<span class="hljs-string">"3"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Grid.ColumnSpan</span>=<span class="hljs-string">"2"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Content</span>=<span class="hljs-string">"Suchen"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">...</span> /></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">DatePicker</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Grid.Row</span>=<span class="hljs-string">"2"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Width</span>=<span class="hljs-string">"300"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">HorizontalAlignment</span>=<span class="hljs-string">"Center"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Header</span>=<span class="hljs-string">"Datum:"</span> /></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">TimePicker</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Grid.Row</span>=<span class="hljs-string">"2"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Grid.Column</span>=<span class="hljs-string">"1"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Header</span>=<span class="hljs-string">"Uhrzeit:"</span> /></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">TextBox</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Grid.Row</span>=<span class="hljs-string">"1"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">PlaceholderText</span>=<span class="hljs-string">"von"</span> /></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">TextBox</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Grid.Row</span>=<span class="hljs-string">"1"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Grid.Column</span>=<span class="hljs-string">"1"</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">PlaceholderText</span>=<span class="hljs-string">"nach"</span> /></span></span><br/><span class="xml">      <span class="hljs-tag"><<span class="hljs-name">TextBlock</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Text</span>=<span class="hljs-string">"Fahrplanauskunft"</span> /></span></span><br/><span class="xml">    <span class="hljs-tag"></<span class="hljs-name">Grid</span>></span></span><br/><span class="xml">    <span class="hljs-tag"><<span class="hljs-name">StackPanel</span></span></span><br/><span class="xml"><span class="hljs-tag">      <span class="hljs-attr">Grid.Row</span>=<span class="hljs-string">"1"</span></span></span><br/><span class="xml"><span class="hljs-tag">      <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">      <<span class="hljs-attr">TextBlock</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">...</span></span></span><br/><span class="xml"><span class="hljs-tag">        <span class="hljs-attr">Text</span>=<span class="hljs-string">"Aktuell liegen keine Störungen vor"</span> /></span></span><br/><span class="xml">    <span class="hljs-tag"></<span class="hljs-name">StackPanel</span>></span></span><br/><span class="xml">  <span class="hljs-tag"></<span class="hljs-name">Grid</span>></span></span><br/><span class="xml"><span class="hljs-tag"></<span class="hljs-name">Page</span>></span></span> 

Quellcode

Den Quellcode zum Beispielprojekt, also die gestaltete Seite „Fahrplan“ in Form einer minimalistischen App, finden Sie auf der Webseite der Autoren unter [15].

Fazit und Ausblick

In diesem Teil der Artikelserie standen die User-Interface-Controls im Fokus. Sie sind die kleinsten Einheiten, aus denen die Entwickler nach den Vorgaben der Designer die App wie bei einem Baukastensystem zusammensetzen. Mithilfe der Controls und des Layouts wird die Funktionalität der App bestimmt. Auch die Art und Weise der Bedienung legen wir darüber fest. Und wie gelingt ein ansprechendes Aussehen der App? Das ist eine Frage des Designs. Dazu gehören Farben, Materialien, Schriftarten und Animation. Neben einem modernen Look-and-feel gilt es auch, das Corporate Design zu beachten, das bei Business-Apps durch den Kunden vorgegeben wird. Diese Themen sind Gegenstand des vierten und nächsten Teils dieser Artikelserie.

Fussnoten

  1. [1] Microsoft Learn, Steuerelemente für Windows-Apps, http://www.dotnetpro.de/SL2311WinUI3_1
  2. [2] Syncfusion, A state-of-the-art WinUI toolkit to build Windows Desktop apps, http://www.dotnetpro.de/SL2311WinUI3_2
  3. [3] Microsoft Learn, Bewegung für Windows-Apps, http://www.dotnetpro.de/SL2311WinUI3_3
  4. [4] WinUI 3 Controls by DevExpress, http://www.dotnetpro.de/SL2311WinUI3_4
  5. [5] Telerik UI for WinUI, http://www.dotnetpro.de/SL2311WinUI3_5
  6. [6] GrapeCity, WinUI Controls for the Windows Desktop, http://www.dotnetpro.de/SL2311WinUI3_6
  7. [7] Microsoft Learn, Windows Community Toolkit Documentation, http://www.dotnetpro.de/SL2311WinUI3_7
  8. [8] XAML Brewer by Diederik Krols, Using the Windows Community Toolkit DataGrid with WinUI 3 and Entity Framework Core, http://www.dotnetpro.de/SL2311WinUI3_8
  9. [9] Syncfusion, WinUI DataGrid Overview, http://www.dotnetpro.de/SL2311WinUI3_9
  10. GrapeCity, The Most Complete WinUI Datagrid Control, http://www.dotnetpro.de/SL2311WinUI3_10
  11. Telerik UI for WinUI, Getting Started with WinUI DataGrid, http://www.dotnetpro.de/SL2311WinUI3_11
  12. DevExpress WinUI Controls, Data Grid, http://www.dotnetpro.de/SL2311WinUI3_12
  13. Elena Bochkor, Veikko Krypczyk, Das App-Layout, dotnetpro 10/2023, Seite 40 ff., http://www.dotnetpro.de/A2310WinUI3
  14. Windows Community Toolkit Labs bei GitHub, http://www.dotnetpro.de/SL2311WinUI3_13
  15. Quellcode zum Beispielprojekt, https://wp.larinet.com

Neueste Beiträge

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Evolutionäres Prototyping von Business-Apps - Low Code/No Code und KI mit Power Apps
Microsoft baut Power Apps zunehmend mit Features aus, um die Low-Code-/No-Code-Welt mit der KI und der professionellen Programmierung zu verbinden.
19 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige