Posts mit dem Label WPF werden angezeigt. Alle Posts anzeigen
Posts mit dem Label WPF werden angezeigt. Alle Posts anzeigen

Montag, 22. August 2022

Tool Lagerverwaltung (Teil 2) Neue Solution


Welche Architektur oder welche Form soll das Projekt haben. Am liebsten setze ich auf die Drei-Schichten-Architektur. Klinkt abstrakt und hochtrabend, aber kompliziert ist die Sache nicht. Sich dran halten ist anfangs schwierig. Vorzugsweise setze ich auf Desktop Anwendung.

Benötigt

  • Visual Studio 2022 oder anderen Compiler
  • .NET 6.0
  • Codexzier's Application Framework
  • GitHub

 

Ziel für diesen Blog-Eintrag

Eine neue Solution und die benötigten Projekte einrichten mit dem Ansatz der Drei-Schichten Architektur..

 

Drei-Schichten

Wenn ich Rückblicke, in welchen Formen ein Drei-Schichten Modell aussieht, dann waren diese immer unterschiedlich gestaltet und hielten dennoch erkennbar Drei Schichten. Grundlegen haben wir das Frontend oder auch Benutzeroberfläche genannt, dann die Service-Schicht in der die Daten verarbeitet werden und als drittes die Datenhaltung in einer Datenbank.

 

Solution anlegen

Aber bevor das Projekt angelegt wird, soll eine leere Solution erstellt werden. Idealerweise gibt ihr in die Suche 'Blank' ein.

Nach der Auswahl kommen wir zur Eingabe des Solution Name und das Festlegen des Speicherortes.


Git Repository

Bevor das WPF Projekt Eingesetzt wird, gehört die Pflege der Sourcecodes in das Git System. Also fehlt der Klick auf den Button 'Create Git Repository…'.

Falls die Anmeldung von GitHub noch nicht geschehen ist, dann wird dies jetzt gefordert. In meinem Fall ist das Projekt öffentlich, weshalb der Haken für 'Private repository' raus ist.

Ist das Repository erstellt, dann sollte der aktuelle Stand bereits Online in eurem GitHub Repositories zu finde sein.

 

Einrichten der Projekte

Als erstes kommt das WPF Projekt, das die Frontend Schicht abbildet. Hier verwende ich das Projekt Template, dass bereits die Referenzen zu meinem Application Framework enthält.

Im folgenden kommen zwei Möglichkeiten, um das Template in Visual Studio 2022 einzubinden.

 

Option 1

Die Zip-Datei Codexzier Application Framework Vorlage Augsut 2022 in den Ordner ../Visual Studio 2022/Templates/ProjectTemplates einfügen (zum Download)

 

Option 2

Das Codexzier Application Framework von meinem GitHub Repository herunterladen und in Visual Studio öffnen. Dann oben auf Projekt

Dann das Projekt Template 'Codexzier.Wpf.ApplicationTemplate' auswählen..

..und im nächsten Schritt einen Namen vergeben.

Wählt nun die Solution aus und fügt diesem ein neues Projekt zu.

Das WPF Vorlagen Projekt kann bei Bedarf über die Suche gefunden werden.

Für die Benutzer Oberfläche, solltet ihr für den Namensraum den Bereich Namentlich erkenntlich Beschreiben.


Service Schicht

Das nächste Projekt soll alle Service Komponenten enthalten, die nichts mit der Benutzer Oberfläche zu tun haben. Obwohl ich von Service Schicht gesprochen habe, wird eine einfachen Klassen Bibliothek angelegt. Hier darauf achten, dass ihr die Vorlage für das aktuelle .NET Framework verwendet.

Und wieder einen passen Namen vergeben für die Service Komponenten Schicht.

Im nächsten Schritt muss noch die .NET Version angegeben werden und dann kann das Projekt erstellt werden.


Ein Projekt noch

Eins fehlt noch und zwar das für die Automatischen Tests. Für die UI kann eigentlich auch ein Test Projekt angelegt werden, aber aktuell reicht ein Unit Test Projekt für die Service Komponenten Schicht.

Der Namespace bekommt den Zusatz 'Test'. Damit sollte das Unit Test Projekt direkt unter dem zu testenden Projekt sein.

Zum Schluss wieder die .NET Version auswählen und erstellen.


Initialen Stand sichern

Hier braucht der Text schlichtweg aussagen, dass grundlegend die Projekte angelegt wurden.


Grundlage für das gesamt Projekt

Der zweite Schritt ist erledigt. Eine neue Solution mit den Grundlegenden Projekten ist angelegt. Die Inhalte habe ich nicht zu stark unterteilt, schließlich handelt es sich hier um ein sehr kleines Privat Projekt. Im nächsten Schritt geht’s dann mit dem Konzept und Funktionsumfang weiter.


Übersicht

Dienstag, 2. August 2022

Tool Lagerverwaltung (Teil 1) Wiki anlegen in GitHub

Für das Tool soll das eigene Framework verwendet werden, dass ich in den letzten Jahren immer weiter ausgebaut hatte. Jedoch ist noch offen, eine Dokumentation anzulegen, die den Funktionsumfang sowie Verwendung des Applikation Framework zeigt.

 

Benötigt

  • Visual Studio 2022 oder anderen Compiler
  • .NET 6.0
  • Codexzier's Application Framework
  • GitHub

 

Was ist mein Ziel?

Primär soll ein einfaches Tool entwickelt werden, dass zur Lagerverwaltung verwendet werden kann. Mit der der Entwicklung möchte ich beschreiben, welche Vorgehensweisen und Lösungen ich verwende. Zudem soll zu jeder Funktion aus dem Applikation Framework dokumentiert werden, das ich für den aktuellen Schritt verwendet wird.

 

Neben Produkt

Immer wenn ich eine neue Anwendung geschrieben habe, benötigte ich Grundlegende Inhalte für eine WPF Anwendung mit bestimmten Funktionen die ich selbst mal geschrieben habe. Und weil ich meine Anwendung auch ein Bestimmtes Aussehen haben sollen, habe ich auch die Styles immer mit kopiert. Damit ergaben sich Vorteile aber auch Nachteile.

 

Vorteile

  • Auf den Bisherigen Lösungen etwas Besseres oder neues entwickeln
  • Übung, Übung, Übung

Nachteil

  • Pflege älterer Anwendung erschwert
  • Doppelte Arbeit

 

Ok, gehen wir von der selbst Erklärung rüber zum ersten Schritt.

 

Henne-Ei-Problem

Fange ich mit der Dokumentation an, was funktionieren soll oder fange ich mit dem Programmieren an und dokumentiere? Die Frage, ob man mit der Dokumentation anfängt, ist davon abhängig, um was für eine Dokumentation angelegt werden soll. Bevor etwas entsteht, kann im Grunde nur ein Konzept- oder ein Grundbeschreibungen zu einer Anwendung angelegt werden.

In meinem Fall liegt der Programmcode vor und die Dokumentationsbeschreibung fehlt. Also wie in vielen Projekten.

 

GitHub Wiki

Die Sourcen zu dem Applikation Framework von mir habe ich bereits auf GitHub hochgeladen und dort kann zusätzlich ein Wiki gepflegt werden. Und damit soll's neben dem Hauptprojekt beginnen.

 

Als erstes hilft eine Grobe Inhaltsangabe anzulegen, so dass eine Grundlage vorliegt, an der man sich orientieren kann.

 Home  
 Lizenz  
 Grundlagen  
      Vorlage  
      Neues Projekt einrichten  
      Grundaufbau  
      Weiter Einstellungen  
 Components  
      Event Bus Manager  
      User Settings  
      Animation Helper  
 Styles  
      Blue Gray  
      White Gray  
      Gray White  
 Steuerelemente  
      Button  
      Diagram  
      Folder Browser  
 Game Tree 

So sieht der Text in der Bearbeitung aus in der GitHub Wiki (Zuvor hatte ich am Ende jedem Eintrag immer Page stehen gehabt, leider ist das dann auch der Name der Seite und habe das deshalb abgeändert)

 * [[Lizenz|Lizenz]]  
 * [[Grundlagen|Grundlagen]]  
 > - [[Vorlage|Grundlagen - Vorlage]]  
 > - [[Neues Projekt einrichten|Grundlagen - Neues Projekt einrichten]]  
 > - [[Grundaufbau|Grundlagen Grundaufbau]]  
 > - [[Weiter Einstellungen|Grundlagen - Weitre Einstellungen]]  
 - [[Components|Components]]  
 > - [[Event Bus Manager|Components - Event Bus Manager]]  
 > - [[User Settings|Components - User Settings]]  
 > - [[Animation Helper|Components - Animation Helper]]  
 * [[Styles|Styles]]  
 > - [[Blue Gray|Styles - Blue Gray]]  
 > - [[White Gray|Styles - White Gray]]  
 > - [[Gray White|Styles - Gray White]]  
 * [[Steuerelemente|Steuerelemente]]  
 > - [[Button|Steuerelemente - Button]]  
 > - [[Diagram|Steuerelemente - Diagram]]  
 > - [[Folder Browser|Steuerelemente - Folder Browser]]  
 > - [[Game Tree|Steuerelemente - Game Tree]]  

Und so sieht's dann nach dem Speichern aus.



Gefärbter Text
Im Bild ist zu sehen, dass nur der Text 'Lizenz' in blauer schrift hinterlegt ist. Wenn du suggestiv einen Link vermutest, dann liegst du richtig. Für die Seite Lizenz, habe ich bereits eine Seite angelegt. Alle die noch rot eingefärbten sind, müssen noch erstellt werden. Wenn man auf eines der rot gefärbten Text klickt, kommt statt einem Fehler eine Bearbeitungsmaske, um diese Seite zu erstellen.


 

Grundlage

Der erste Schritt ist damit getan. Nun muss man nur dranbleiben und hin und wieder schauen, ob die Struktur weiterhin passt.


Links

https://docs.github.com/en/communities/documenting-your-project-with-wikis/editing-wiki-content

 

Dienstag, 2. Februar 2021

Balkendiagramm mit WPF


Seit letztem Jahr habe ich an meinem kleinen Programm gebastelt, mit dem man die RKI Daten sichten kann.

Diesmal habe ich die Darstellung eines Balkendiagramms gewählt, welches mit wenigen Schritten realisierbar ist.

  

Anforderung

Ein neues Steuerelement soll so erstellt werden, dass es im eigenen Projekt immer wieder verwendet werden kann. Hier soll eine Liste mit Werten möglich sein, in das Bar Diagramm zu ‚binden‘. Die Anzahl der Elemente bestimmt die Breite des einzelnen Balken und der Wert wiederum die Höhe.

 

Neues Steuerelement anlegen

Das Anlegen des neue Steuerelements sollte in einem eigenen Order erfolgen. Mit rechter Maustaste und dann im Kontext-Menü Add -> User Control (WPF)… beginnen wir mit dem Benutzersteuerelement.



Gebt einen Namen für das Steuerelement ein,  hier empfehle ich am Ende Control hinzuzufügen. In einigen Projekten werden z.B. benutzerdefinierte Steuerelemente und Ansichten (Views) unterschieden.


 

Nach dem Erstellen sollte unter der .XAML- auch eine .XAML.cs-Datei bestehen. In diese werden die Dependency Properties hinzugefügt, mit dem es später möglich ist, die Liste mit Werten zu ‚binden‘.

 


Zunächst brauchen wir im XAML Code ein StackPanel, in dem die Werte als Balken abgebildet werden. Idealerweise kann das Grid gegen ein Border Element ausgetauscht werden. Auch ein Rand in der Farbe eurer Wahl sollte nicht fehlen. Früher oder später stellt man fest, dass der Rand zur Orientierung hilft, das Diagramm in seinen Grenzen zu lesen.

Das StackPanel wird direkt in das Border Element untergeordnet. Hier muss die Ausrichtung der Orientierung und die Flow Direction von links nach rechts festgelegt werden. Da die Balken von links beginnen, muss das Ausrichten horizontal nach links ausgerichtet sein. Zur Erleichterung bekommt das StackPanel einen Namen zugewiesen, der über den Codeabschnitt verwendet wird. Das erleichtert das Hinzufügen der Balken.

 <UserControl x:Class="ExampleBarDiagram.Diagram.BarDiagramControl"  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   
        mc:Ignorable="d"   
        d:DesignHeight="450" d:DesignWidth="800"  
        SizeChanged="UserControl_SizeChanged">  
   <Border BorderThickness="1"   
       BorderBrush="Chartreuse"   
       Margin="0">  
     <StackPanel x:Name="SimpleDiagram"   
             Orientation="Horizontal"   
             FlowDirection="LeftToRight"   
             HorizontalAlignment="Left" />  
   </Border>  
 </UserControl>  

Für die Liste mit dem Werten empfiehlt sich das Anlegen eines Datenobjektes mit dem Namen DiagramLevelItem. Dieser kann zum einen den double Wert sowie einen Text aufnehmen. Neben dem Anlegen der Property wird die Static readonly Dependency angelegt und ist nur für diese Klasse gültig. In der PropertyMetadata wird eine neue Liste erstellt, falls diese nicht zugewiesen wurde, aber versucht wird diese abzurufen. Mit dem zweiten Parameter wird eine Static Methode übergeben, die ausgeführt wird, wenn sich der Wert der Property geändert hat.

   public partial class BarDiagramControl  
   {  
     public List<DiagramLevelItem> DiagramLevelItemsSource  
     {  
       get => (List<DiagramLevelItem>)this.GetValue(DiagramLevelItemsSourceProperty);  
       set => this.SetValue(DiagramLevelItemsSourceProperty, value);  
     }  
     public static readonly DependencyProperty DiagramLevelItemsSourceProperty =  
       DependencyProperty.RegisterAttached("DiagramLevelItemsSource",  
         typeof(List<DiagramLevelItem>),  
         typeof(BarDiagramControl),  
         new PropertyMetadata(new List<DiagramLevelItem>(), UpdateDiagram));  
     private static void UpdateDiagram(DependencyObject d, DependencyPropertyChangedEventArgs e)  
     {  
       if (d is BarDiagramControl control)  
       {  
         SetValuesToBarDiagram(control);  
       }  
     }  
 ... 

Der Hauptteil für das Ausfüllen des StackPanel wird schließlich mit der Methode SetValuesToBarDiagram(BarDiagramControl control) ermöglicht. Hier wird für die aktuelle Höhe die Skalierung der einzelnen Werte in Balken verwendet, damit die Balken sich an die verwendete Größe des Steuerelements anpassen.

Die einzelnen Balken sind schlicht aus Rectangles erstellt,  die mit einer Farbe gefüllt sind. Ausgerichtet sind sie immer nach unten und die Höhe wird aus dem skalierten Wert gesetzt. Die Breite selbst wird in Abhängigkeit der Breite des Steuerelementes und der Anzahl der Werte festgelegt.

Mit dem gegeben Namen des StackPanels, kann über die Property Children das neue Rectangle Instanz hinzugefügt werden.

 ...  
    private static void SetValuesToBarDiagram(BarDiagramControl control)  
     {  
       if (control.DiagramLevelItemsSource == null)  
       {  
         return;  
       }  
       control.SimpleDiagram.Children.Clear();  
       var heightScale = control.ActualHeight / 100d;  
       var widthPerResult = control.ActualWidth / control.DiagramLevelItemsSource.Count;  
       foreach (var diagramLevelItem in control.DiagramLevelItemsSource)  
       {  
         var heightValue = diagramLevelItem.Value * heightScale;  
         var barItem = new Rectangle  
         {  
           Fill = new SolidColorBrush(Colors.DarkGreen),  
           VerticalAlignment = VerticalAlignment.Bottom,  
           Width = widthPerResult,  
           Height = heightValue,  
           ToolTip = diagramLevelItem.ToolTipText  
         };  
         control.SimpleDiagram.Children.Add(barItem);  
       }  
     }  
 ...   

Damit die Balken beim Verändern der Größe des Steuerelementes angepasst werden, wird auf das Event SizeChanged zurückgegriffen. Ab hier wird dann auch sichtbar, warum der Codeanteil separat in einer Methode aufgeführt wurde.

 ...  
     private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) => SetValuesToBarDiagram(this);  
   }   


Fast fertig

Im Grunde ist das neue Steuerelement - Diagram fertig und kann im Projekt eingesetzt werden. In diesem Beispiel wird das Steuerelement in das MainWindow.xaml hinzugefügt. Das Projekt muss einmal gebaut werden, damit das Steuerelement über die Toolbox, per klicken und ziehen in die Zielansicht gezogen werden kann.

 

Im Beispiel wurde für MainWindow.xaml ein ViewModel angelegt, dass das Interface von INotifyPeropertyChanged implementiert. Neben der Liste mit den Werten (DiagramLevelItem) kommt eine Property CommandCreateNewListWithValues mit dem Interface ICommand.

public class MainWindowViewModel : INotifyPropertyChanged  
 {  
   private List<DiagramLevelItem> _items;  
   private ICommand _commandCreateNewListWithValues;  
   public List<DiagramLevelItem> Items  
   {  
     get => this._items;  
     set  
     {  
       if (!Equals(value, this._items))  
       {  
         this._items = value;  
         this.OnPropertyChanged();  
       }  
     }  
   }  
   public ICommand CommandCreateNewListWithValues  
   {  
     get => this._commandCreateNewListWithValues;  
     set  
     {  
       if (Equals(value, this._commandCreateNewListWithValues)) return;  
       this._commandCreateNewListWithValues = value;  
       this.OnPropertyChanged();  
     }  
   }  
   public event PropertyChangedEventHandler PropertyChanged;  
   [NotifyPropertyChangedInvocator]  
   protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)  
   {  
     this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
   }  
 }  

Das ViewModel muss zuerst in den Window.DataContext eingetragen werden. Nun kann die Liste mit dem Diagram über die DependencyProperty DiagramLevelItemsSource gebunden werden. Als zweites kommt noch ein Button hinzu, der über Command die zweite Property vom Typ IComand bindet.

<Window  
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
     xmlns:local="clr-namespace:ExampleBarDiagram"  
     xmlns:Diagram="clr-namespace:ExampleBarDiagram.Diagram"  
     x:Class="ExampleBarDiagram.MainWindow"  
     mc:Ignorable="d"  
     Title="Example Bar Diagram" Height="450" Width="800">  
   <Window.DataContext>  
     <local:MainWindowViewModel />  
   </Window.DataContext>  
   <Grid>  
     <Grid.RowDefinitions>  
       <RowDefinition Height="*" />  
       <RowDefinition Height="Auto" />  
     </Grid.RowDefinitions>  
     <Diagram:BarDiagramControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" DiagramLevelItemsSource="{Binding Items}"/>  
     <Button Content="create new list with values" Grid.Row="1" Command="{Binding CommandCreateNewListWithValues}" />  
   </Grid>  
 </Window>  

Über den Behind code MainWindow.xaml.cs wird im Konstruktor nun das ViewModel aus dem DataKontext abgerufen. Hier muss zum einen für den Button Command die Instanz erstellt werden und, zum anderen über dem Parameter wiederum das ViewModel übergeben werden. Hierfür erstellen wir die Klasse ButtonCommandCreateNewListWithValues.

 public partial class MainWindow : Window  
 {  
   public MainWindow()  
   {  
     this.InitializeComponent();  
     var viewModel = (MainWindowViewModel) this.DataContext;  
     viewModel.CommandCreateNewListWithValues = new ButtonCommandCreateNewListWithValues(viewModel);  
   }  
 }  

Die Klasse ButtonCommandCreateNewListWithValues implementiert das Interface IComand. Über den Konstruktor wird das übergebene ViewModel in den Private Member referenziert. Die Methode CanExecute wird eigentlich nicht verwendet und gibt da her nur true zurück.

Mit der Methode Execute wird mit der Random Klasse die Werte erzeugt. Da das Diagramm für Werte bis 100 ausgelegt ist, sollte der maximale Wert des Einzelnen auch bei 100 sein. Die Anzahl der angelegten Werte sollte sich darauf begrenzen, wie viele Pixel in der Breite für das Diagram zur Verfügung stehen. Die Liste mit den Werten wird erst am Ende zugewiesen, da sonst die PropertyChanged nicht ausgeführt wird.

 

Schlusswort

Die Ausführung ist schon sehr kompakt und lässt sich auch noch etwas kürzer fassen. Einige Stellen habe ich aus gewissen Gründen mehr ausgebaut als nötig. Denn ich will in weiteren Blog Einträgen beschreiben, wie man dieses Steuerelement weiter ausbauen kann und dies natürlich in kurzen Schritten.

Das Beispiel ist eine entkoppelte Darstellung des verwenden ‚Steuerelement‘ Diagram, welches ich bereits in einem anderem kleinen privaten Projekt verwende.

 

Wie immer habe ich die Solution auf Github hochgeladen:

Github: ExampleBarDiagram

 

Montag, 30. November 2020

RKI Daten einlesen und Ausgeben


Dieses Jahr ist anders. Anders, weil unser Alltag immer irgendwie mit Corona zu tun hat. Wir halten Abstand, wir tragen Masken und hoffen, dass die Pandemie in den Griff bekommen wird. Manchmal habe ich Zweifel, aber die Zweifel sind trügerisch und führen zu einem Paradoxen Gedankenweg. Statt zu denken, wir können lockerer mit den Regeln der Pandemie umgehen, weil wir hier nicht so stark betroffen sind - wie andere Teile von Europa - sollten wir daran denken, dass wir hier in Deutschland besser dran sind, da große Teile der Bevölkerung die AHA Regeln ernst nehmen und berücksichtigen.

 

Die Daten des RKI, die wir immer wieder auf verschiedenen Seiten sehen, kommen von der API Schnittstelle NPGEO.  Auf der Webseite des NPGEO https://npgeo-corona-npgeo-de.hub.arcgis.com/ können wir auf die zentralen Daten der Pandemie zugreifen. Ein Dashboard zeigt uns die aktuellen Zahlen für Deutschland. Und da setze ich an, denn mein Thema für diesen Blogeintrag ist der Abruf der Daten und wie man diese aufbereiten kann.

 

Benötigt:

Visual Studio 2019

 

Anforderung

Über die NPGEO API Explorer soll eine Abfrage zusammengestellt werden. Die heruntergeladenen Daten sollen vom JSON Format eingelesen und in ein Datenobjekt gefüllt werden. Für die Darstellung soll eine Liste mit den Namen der Städten und die 7-Tage-Inzidenz wiedergegeben werden.

 

API Explorer vom RKI

Aktuell muss man etwas herunter scrollen, bis man an die Links mit den freizugänglichen Daten kommt. Uns interessiert der Link zum Robert Koch-Institut.

 

 

Auf der nächsten Seite wählen wir die Datenquelle 'RKI Corona Landkreise' aus und wechseln dort von der Übersicht auf den Tab API-Explorer. Im Rahmen für die Abfrage nehmen wir alle Haken raus, außer für 'cases7_per100k' (7 Tage Inzidenz) und 'GEN' (Name des Landkreises).

 


In Ausgabeoption kann die Rückgabegeometrie abgestellt werden. Anschließend kopieren wir die fertige Abfrage-URL, die  rechts auf der Seite angezeigt wird.



Datenformat betrachten (API-Explorer)

Unten, mit dem Button 'Testen', können die Daten im JSON-Format betrachten werden. Ich weiß nicht, ob ich an dieser Stelle etwas falsch mache, aber bei mir kamen mit den Einstellungen nicht die Daten, die konfiguriert wurden, an. Daher setzte ich auf einen alternativen Weg.



 

Datenformat betrachten (Abfrage-URL)

Die Abfrage-URL können wir über den Browser in einem neuen Tab abrufen und die Daten in einem unformatierten JSON-Format bekommen. Das Ergebnis anschließend speichern mit dem Kontext Menü 'Speichern unter…'. Die Datei sollte beim Benennen mit '.json' enden, damit diese Datei beim Öffnen mit Visual Studio Code oder Notepad++ richtig erkannt wird (ggf. plugin oder Extension installieren, damit eine Formatierung für JSON funktioniert, kann sein, dass die Formatierung auch manuell angetriggert werden muss).

 

WPF Anwendung

Inzwischen kann auch mit .NET Core eine WPF Anwendung geschrieben werden. Für den folgenden Aufbau macht dies jedoch keinen Unterschied, da nur grundlegende XAML Elemente verwendet werden, könnte auch eine UWP Anwendung in Frage kommen.

 

Datenobjekt

Nach dem Erstellen benötigen wir folgenden Datenobjekte.

 

 public class RkiDataResult  
 {  
   public Landkreis[] features { get; set; }  
   public class Landkreis  
   {  
     public Attribute attributes { get; set; }  
     public class Attribute  
     {  
       public string GEN { get; set; }  
       public double cases7_per_100k { get; set; }  
     }  
   }  
 } 

 

Abruf mit WebClient

Hier muss lediglich die Abfrage-URL für den Download eingetragen werden. Die Daten selbst werden dann mit dem 'JsonConvert' Klasse in das zuvor angelegte Datenobjekt umgewandelt. (Die Bibliotek Newtonsoft für 'JsonConvert' wird aus den Nuget Paketen in das Projekt hinzugefügt).


 public class RkiWebClientComponent  
 {  
   public static RkiDataResult LoadAktualData()  
   {  
     var client = new WebClient();  
     client.Headers[HttpRequestHeader.ContentType] = "application/json";  
     try  
     {  
       var urlAddress = "https://services7.arcgis.com/mOBPykOjAyBO2ZKk/arcgis/rest/services/RKI_Landkreisdaten/FeatureServer/0/query?where=1%3D1&outFields=GEN,cases7_per_100k&returnGeometry=false&outSR=4326&f=json";  
       var result = client.DownloadString(urlAddress);  
       return JsonConvert.DeserializeObject<RkiDataResult>(result);  
     }  
     catch (Exception e)  
     {  
       throw e;  
     }  
   }  
 } 

 

Auflisten und Item-Template

Das Grid in MainWindow wird ersetzt durch eine ListBox, welche einen Namen zugewiesen bekommt. Das Template zu einem Item ermöglicht die Informationen zu platzieren. Die Darstellung des 7-Tage-Inzidenz Wert reicht aus (mit einer Nachkommastelle) daher wird im Binding noch ein StringFormat N1 gesetzt. Für das deutsche Zahlenformat, ist das Setzen der Language auf 'DE' erforderlich.


 <Window x:Class="ExampleSimpleReadRkiData.MainWindow"  
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
     mc:Ignorable="d"  
     Title="Aktuelle RKI Daten"   
     Height="250" Width="400"   
     Language="DE">  
   <ListBox x:Name="LandkreiseListe"   
        HorizontalContentAlignment="Stretch">  
     <ListBox.ItemTemplate>  
       <DataTemplate DataType="RkiDataResult.Landkreis.Attributes">  
         <Grid>  
           <Grid.ColumnDefinitions>  
             <ColumnDefinition Width="*" />  
             <ColumnDefinition Width="*" />  
           </Grid.ColumnDefinitions>  
           <TextBlock Grid.Column="0"   
                 Text="{Binding GEN}"  
                 Margin="0 0 10 0"/>  
           <TextBlock Grid.Column="1"   
                 Text="{Binding cases7_per_100k, StringFormat=N1}"   
                 HorizontalAlignment="Right" />  
         </Grid>  
       </DataTemplate>  
     </ListBox.ItemTemplate>  
   </ListBox>  
 </Window>  

 

Abruf und in die View laden

In den Behind-Code oder bzw. in die MainWindow.xaml.cs werden die Daten abgerufen und in die ListBox geladen. Diese Aufgabe wird von der Methode 'OnApplyTemplate' übernommen, die hier überschrieben wird. Die Auflistung kann in diesem Zug auch gleich sortiert werden, nach dem 7-Tage-Inzidenz.

 

 public partial class MainWindow : Window  
 {  
   public MainWindow() => this.InitializeComponent();  
   public override void OnApplyTemplate()  
   {  
     var rkiData = RkiWebClientComponent.LoadAktualData();  
     this.LandkreiseListe.ItemsSource = rkiData  
       .features  
       .OrderByDescending(o => o.attributes.cases7_per_100k)  
       .Select(s => s.attributes);  
   }  
 } 

 

Fertig

Der interessante Teil ist, woher man die Daten bekommt und wie man das programmatisch abrufen kann.

Nun, das Ganze lässt sich weiterausbauen und das habe ich auch getan. Auf meinem Github Repository findet ihr dieses Beispiel und eine weitere ausgebaute Version. Diese kann die Daten in der Liste sortieren, nach zwei verschiedenen Werten. Die Daten zu einem District werden zusätzlich in einem chronologischen Diagramm angezeigt. Zu dem speichert die Anwendung die tagesaktuellen Daten, womit eine Historie mit dem Diagramm abgebildet werden kann.

 

Beispiel wie immer

Das Ergebnis kann wieder von meinem Github Repository heruntergeladen werden. 

Github - ExampleReadAndShowRkiData




Altes Beispiel was ich mal angefangen hatte

Ein alteres Beispiel, dass die Daten von einemn zwischengespeicherten Datenhalter kommt. Siehe dazu mehr auf rki.marlon-lueckert.de . Im gegensatz zu dem Beispiel, werden die Daten Lokal gespeichert und ermöglich eine eigene Historie abzubilden in Form eines Diagrames.

Github - ExampleReadAndShowRki (Old Example)


Aufwendinge UI mit den selben Funktionen

Der Folgende Branch ist eine Übung bezüglich die Anwendung mehr Ansehnlicher zu gestalten. Hat aber nicht mehr Funktionen als mein altes Beispiel (aber auch nicht alle). Bezieht jedoch die Daten direkt vom RKI und nicht durch einen zwischen Datenhalter. Also wie aus dem Beispiel bereits beschrieben. 

Github - OverviewRkiData


 

Eine weitere API Quellen zum Thema Corona

Der folgende Link führt zu einer weiteren Quelle. Für die, die sich lieber die internationalen Daten ansehen wollen. Mit der Doku über POSTMAN, werden dort auch schon fertige Code Snippeds angezeigt.

 

Corona COVID 19 API (POSTMAN)


Montag, 25. November 2019

USB Experiement Interface board von Velleman


Diesmal dürfte das etwas mehr retro (eine Zeitreise - back in time) sein.

Dieses Board kaufte ich vor 14 Jahren, als ich noch in der Ausbildung war. Mit dem Programmieren kam ich bis dato schlecht voran, aber ich wollte das können. Visual Studio 2005 war zu der Zeit aktuell und die Sprache C# programmierte ich noch sehr anfänglich. Ziel war damals einen Roboterarm zu bauen, aus dem nie was wurde. Dennoch wusste ich, welche Dinge ich zunächst lernen musste, damit ich mich meinem Ziel nähern konnte. Doch bevor ich damals mit der beiliegenden Test-Software etwas anstellen konnte, musste der Bausatz zusammengelötet werden.
Nun kam das Wochenende und ich suchte eine Lösung in C# und wurde nach langer Suche fündig. Ich fand einen Beispielprogrammcode, wie ich auf die beiliegende DLL zugreifen konnte und somit das Board auslesen und steuern konnte. Fast das ganze Wochenende verbrachte ich damals mit dem Programmieren eines eigenen Tools, dass auf C#, .NET Framework 2.0 und WindowsForms setzte.


Gegenwart
Aus heutiger Sicht, war der Programmcode, den ich damals geschrieben hatte, sehr einfach und dient nicht gerade als Beispiel-Programm. Deshalb entschied ich mich ein neues Beispiel-Programm zu schreiben, bevor ich das Board jetzt zeitnah bei eBay zum Verkauf anbiete.

Ein Teil davon wird neu sein
Ich fand in meinen Sicherung tatsächlich mein erstes Programm wieder, aber was ich an Programmcode entdeckte, sollte auch in der Sicherung bleiben. Deshalb kopierte ich den wesentlichen Programmcode. Für die GUI verwende ich WPF und kein WindowsForms mehr.
Das Ziel ist wieder ein Tool zu haben, dass die Verbindung herstellen kann. Die Steuerung sollte das Einschalten der Ausgänge und das Lesen der Eingänge ermöglichen.

Verbindung zu K8055D.dll
Im folgenden gezeigten Programmcode aus der Klasse ComponentK8055D.cs, wird über das Attribute 'DllImport' auf die Methode zu gegriffen. Die aufgezeigten Methoden greifen auf die Grundfunktionen zu und sind daher die Wesentlichen.

  public class ComponentK8055D : IDisposable  
   {  
     #region add dll  
     [DllImport("K8055D.dll")]  
     private static extern int OpenDevice(int CardAddress);  
     [DllImport("K8055D.dll")]  
     private static extern void CloseDevice();  
     [DllImport("K8055D.dll")]  
     private static extern bool ReadDigitalChannel(int Channel);  
     [DllImport("K8055D.dll")]  
     private static extern void SetDigitalChannel(int Channel);  
     [DllImport("K8055D.dll")]  
     private static extern void ClearDigitalChannel(int Channel);  
     [DllImport("K8055D.dll")]  
     private static extern int ReadAnalogChannel(int Channel);  
     [DllImport("K8055D.dll")]  
     private static extern void OutputAnalogChannel(int Channel, int Data);  
     #endregion  

Der Rest der Klasse führt die Funktionen der DLL aus und fängt Werte ab, die nicht passen. Die Methoden selbst haben entsprechen einen XML Kommentar, um beim Aufruf der Methoden zu zeigen, welche Eingaben für die Parameter gültig sind.

     #region public methods  
     
     public int Open(bool sk5, bool sk6)  
     {  
       int result = 3;  
       result -= sk5 ? 1 : 0;  
       result -= sk6 ? 2 : 0;  
       OpenDevice(result);  
       return result;  
     }  
     
     internal void Close() => CloseDevice();  
     
     public void WriteOutputDigital(int outputNumber, bool setupOnOff)  
     {  
       outputNumber = this.MinMaxValue(outputNumber, 1, 8);  
       if (setupOnOff)  
       {  
         SetDigitalChannel(outputNumber);  
       }  
       else  
       {  
         ClearDigitalChannel(outputNumber);  
       }  
     }  
     
     public void WriteOutputAnalog(int outputNumber, int value)  
     {  
       outputNumber = this.MinMaxValue(outputNumber, 1, 2);  
       value = this.MinMaxValue(value, 0, 255);  
       OutputAnalogChannel(outputNumber, value);  
     }  
     
     public bool ReadDigitalInput(int inputNumber)  
     {  
       inputNumber = this.MinMaxValue(inputNumber, 1, 5);  
       return ReadDigitalChannel(inputNumber);  
     }  
     
     public int ReadAnalogInput(int inputNumber)  
     {  
       inputNumber = this.MinMaxValue(inputNumber, 1, 2);  
       return ReadAnalogChannel(inputNumber);  
     }
  
     #endregion  
     #region private methods
  
     private int MinMaxValue(int value, int limitMin, int limitMax)  
     {  
       if(value < limitMin)  
       {  
         value = limitMin;  
       }  
       if(value > limitMax)  
       {  
         value = limitMax;  
       }  
       return value;  
     }  
     #endregion  
     public void Dispose() => CloseDevice();  
   }  

Der Rest des Programmcodes, dass die Beispiel Anwendung zusammenstellt, hat keine besonderen Aufbau. Daher gehe ich an dieser Stelle nicht weiter ein.

Die Beispiel Anwendung
Mit der Anwendung können nach dem Verbindungsaufbau über USB, die Ein- und Ausgänge des Boards gesteuert oder gelesen werden.
Die Verbindung erfolgt über den Button 'Connect'. Hier ist jedoch zu beachten, wie die Jumper auf dem Board gesetzt sind. Etwas weiter links auf dem Board, ist eine Tabelle zu sehen, die Beschreibt, welche Adresse das Board bekommt, wenn die Jumper gesetzt entsprechend gesetzt wurden. Die vier Adressen ermöglichen auch wirklich vier von solchen Boards an einem PC betreiben zu können.











Intervall Abfrage ist nach dem Start der Anwendung auf 10ms eingestellt. Stellt die häufige Abfrage ein Problem dar, ist zu empfehlen, die Intervall Abfrage auf 100ms oder 1000ms, um zustellen, bevor die Verbindung hergestellt wird.











Hergestellte Verbindung
Nach dem Clicken des Buttons 'Connect' vergehen ein bis zwei Sekunden, bis die Anwendung Ergebnisse Zeigt, wie das Ausmessen der Analog-Eingänge.
Auf dem Board, sind an den Analog-Eingängen zwei Potentiometer, an dem man das Eingangspegel verstellen kann. Wird mit einem Kreuzschlitzschraubendreher daran gedreht, sollte sich in der Anwendung der Balken unter Analog Inputs verändern.










Mit dem Drücken der Buttons, sollten sich auf der GUI für 'Digital Inputs', die Schwarzen Rechtecke in grüne einfärben.












Die Digitalausgänge werden einfach per Checkbox ein- oder ausgeschaltet.

























Bei den analogen Ausgängen, werden die Slider verstellt. Je weiter nach rechts gestellt, umso heller wird die LED am Analogen Ausgang.




















Bekommt man das Board noch
Tatsächlich ja. Also wenn man das Board nicht gerade über eine Ebay Auktion bekommt, sollte der Artikel auch über Conrad Elektronik oder Reichelt zu finden sein. Viel hat sich nicht geändert. Vielleicht die Farbe, aber die Funktionalität dürfte noch die selbe sein. Falls nicht die Ansteuerung mit der LED und dessen Methodennamen sich nicht geändert haben. Das Board, dass ich verwendet habe, hat die Bezeichnung 'P8055-1'. Weicht diese ab, dann sollte man gegebenenfalls die DLL   austauschen und für dies Beispiel-Anwendung durch die mitgelieferte DLL von der CD in die Visual Studio Projekt ersetzen. 

Programmcode wie immer
Natürlich stelle ich den Programmcode wieder auf meinem Github Repository bereit.

Nur Anwendung
Wer nur die Anwendung braucht, kann über diesen Link auch einfach das Programm herunterladen, installieren und dann Ausführen.


Mittwoch, 7. August 2019

Control Style Template mit gleichen Inhalt und unterschieden



Manchmal sind Lösungen so einfach, dass man sie in der Komplexität nicht mehr sieht. So hatte ich tatsächlich eine Menge XAML Code schreiben müssen, die sich praktisch immer wiederholten. Die Premisse war, dass eigentlich nur ein Wert anders war im Template. Die beschriebenen Trigger und Style Aufbauten im Template sind in den meisten fällen identisch für ein Steuerelement. Eine andere Lösung wäre den Aufbau per Behind Code zu schreiben, aber das war tatsächlich die aufwendigere Lösung. Aufwendig, weil mehr code entstehen würde als nötigt.

Umgebung
  • WPF, UWP oder XAML Anwendung

Verschieden und doch gleich
Beide Buttons, sollen die gleichen überschriebenen Animationen erhalten. Der Butten bekommt ein Path Objekt, um die Darstellung für jeden Button zu individualisieren. Dabei soll kein Redundanter Code entstehen.

Los geht’s
Der Ordnung halber, wird der Style in eine eigene Resourcendatei geschrieben. Falls Ihr ein Xaml Path Objekt braucht, gibt es auf dem folgenden Link eine Seite mit vielen verschienden Icons.


Ausgangslage
In diesem Beispiel verwende ich das File-Icon und ein Folder-Icon. Im folgenden XAML wird zunächst gezeigt, wie zwei Styles die fast identisch sind und sich nur in dem eingesetzten Path Control unterscheiden.
Der XAML Auszug kommt aus 'ButtonWithRepeadTemplate.xaml' (Leider lies sich das nicht vernüfitg hier im Blog abbilden, mit ausnahme eines Bildes). Die orange markierten Zeilen, sind die Stellen die sich unterscheiden und grün sind die Teile, dessen Inhalt gleich sind. (Ellipse habe ich nicht mit einbezogen)


Die Lösung ist nahezu offensichtlich, dass dieser Aufbau selbst als Basis Style festegelgt werden kann. Da es sich um einen Wert handelt, kann über die nicht verwendete DependencyProperty 'Content' später die eigentliche Zieländerungen eingesetzt werden. Das sieht dann im folgendem XAML Code aus.
BLAU: Da kann ein Path Element gesetzt werden mit einer Default Zeichnung.
GRÜN: Der Teil, der später nicht ein zweites mal geschrieben werden muss. Wobei hier angemerkt werden muss, dass auch der Teil in der 'Viewbox' nur in der Basis besteht.


Kommen wir zu den Ziel Button Styles, die als Basis Style (BaseOn) von 'ButtonTemplateBase' verwenden. Durch zuweisung eines Path Element in Content, wird das Aussehen in form eines Icon festgelegt. Also der Teil, der wirklich sich unterscheidet.


Ausprobieren
Mit diesen neunen Button Styles geht das Zuweisen auf der Windows.xaml (oder andere View) weiter, um dort die Resource einzubetten und dessen Styles auf die Ziel Buttons festzulegen.


Fertig
Und so sollte das Ergebnis aussen. Beide Buttons sollte die gleichen Trigger Effekte haben.



Zum Schluss
Wie ich bereits am Anfang geschrieben habe, ist die Lösung simpel und sehr überschaubar. Selbst wenn deutlich mehr Wpf Steuerelemente mit dem gleichen Style Grundaufbau kommen, bleibt die Übersicht. 
Aber wie sieht es aus, wenn sich zwei Inhalte im Template unterscheiden? Kurz gesagt, das geht. Aber dazu gehe ich in einem späteren Blogpost darauf ein.

Ameisen Simulation und andere Dinge

Wer lange sich schon mit C# beschäftig, hat sicherlich schon mal was von AntMe gelesen oder gehört. Diese Idee hatte ich aufgegriffen und mi...