Dienstag, 24. Dezember 2013

Frohe Weihnachten

Ich wünsche allen meinen Lesern frohe Weihnachten, ein paar besinnliche Tage und einen guten Rutsch ins neue Jahr.
Hoffen wir, dass möglichst viele Menschen auf der Welt ein unbeschwertes Weihnachtsfest genießen können.

private void Form1_Paint(object sender, PaintEventArgs e)
{
    System.Drawing.Drawing2D.GraphicsPath GP = new System.Drawing.Drawing2D.GraphicsPath();
    GP.AddArc(50, 50, 40, 40, 180, 180);
    GP.AddArc(90, 50, 40, 40, 180, 180);
    GP.AddLine(130, 70, 90, 120);
    GP.AddLine(90, 120, 50, 70);
    e.Graphics.DrawPath(new Pen(new SolidBrush(Color.Red), 3), GP);
}

Freitag, 20. Dezember 2013

Dezimaltrennzeichen herausfinden

Oft kann es von Vorteil oder sogar notwendig sein, das Dezimaltrennzeichen des Systems bzw. des aktuellen Nutzers herauszufinden, um zum Beispiel Eingaben von Zahlen in das richtige Format zu bringen. In C# gelangen wir mit der Klasse CultureInfo an diese Information:

string DecimalSeparator = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;

Donnerstag, 19. Dezember 2013

Android: Dezimaltrennzeichen herausfinden

In vielen Anwendungen möchte man vielleicht Zahlen im korrekten Anzeigeformat der Nutzer darstellen oder Zahlen einlesen.
Da Visual Studio und Xamarin als Dezimaltrenner den Dezimalpunkt (".") verwenden, muss hierbei eventuell je nach verwendetem Trennzeichen des Benutzers eine manuelle Konvertierung der Zahl erfolgen.
Deswegen in diesem Post 2 Zeilen Code, mit denen sich das verwendete Dezimaltrennzeichen des Systems ermitteln lässt:

Java.Text.DecimalFormatSymbols FormatSymbols = new Java.Text.DecimalFormatSymbols();
char DecimalSeparator = FormatSymbols.DecimalSeparator;

Montag, 16. Dezember 2013

Android: Animationen verbinden

Nachdem ich im vorigen Post Gundlegendes zu Animationen in Android erklärt habe, möchte ich heute zeigen, wie man mit einem AnimatorSet mehrere Animationen verbindet und diese nacheinander oder miteinander abspielt.
Das Grundgerüst ist fast identisch zu dem aus dem vorigen Post, nur dieses Mal haben wir 2 TextViews, die nach Klick auf den Button animiert werden sollen:

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

using Android.Animation;

namespace AnimationsExample
{
     [Activity (Label = "AnimationsExample", MainLauncher = true)]
     public class MainActivity : Activity
     {
          protected override void OnCreate (Bundle bundle)
          {
               base.OnCreate (bundle);

               LinearLayout Layout = new LinearLayout (this);
               Layout.Orientation = Orientation.Vertical;

               TextView TxtView1 = new TextView (this);
               TxtView1.Text = "I'm flying";
               Layout.AddView (TxtView1);

               TextView TxtView2 = new TextView (this);
               TxtView2.Text = "I'm flying 2";
               Layout.AddView (TxtView2);

               Button Btn1 = new Button (this);
               Btn1.Text = "Animation";
               Layout.AddView (Btn1);

               Btn1.Click += (object sender, EventArgs e) => {
                    // Animation
               };

               SetContentView (Layout);
          }
     }
}

Nun erstellen wir zuerst, wie im vorigen Post beschrieben, 2 Animationen vom Typ ObjectAnimator, welche jeweils ein TextView nach rechts verschieben:

ObjectAnimator Animator1 = ObjectAnimator.OfInt(TxtView1, "Left", 0, 100);
Animator1.SetDuration(1000);
ObjectAnimator Animator2 = ObjectAnimator.OfInt(TxtView2, "Left", 0, 100);
Animator2.SetDuration(1000);

Um dem Titel gerecht zu werden kommt nun ein Objekt vom Typ AnimatorSet ins Spiel. Von diesem rufen wir zuerst die Methode Play() auf, welche eine Animation als Parameter erwartet. Das Besondere an einem AnimationSet ist nun, dass weitere Animationen aufgerufen werden können. Wir hängen diese entweder durch Aufruf der Methode With() oder Before() an, die Animationen werden dann entweder zusammen mit der ersten Animation oder danach abgespielt. Wie bei einzelnen Animationen auch muss am Schluss Start() aufgerufen werden. Folgender Code bewegt zuerst das obere TextView nach rechts, dann das untere:

ObjectAnimator Animator1 = ObjectAnimator.OfInt(TxtView1, "Left", 0, 100);
Animator1.SetDuration(1000);
ObjectAnimator Animator2 = ObjectAnimator.OfInt(TxtView2, "Left", 0, 100);
Animator2.SetDuration(1000);
AnimatorSet Combined = new AnimatorSet();
Combined.Play(Animator1).Before(Animator2);
Combined.Start();

Zu beachten ist hierbei, dass nur 2 zeitliche Ebenen zur Verfügung stehen, der Aufruf Play(A1).Before(A2).Before(A3) würde also die beiden Animationen A2 und A3 zeitgleich nach Animation A1 abspielen. Soll die Verschachtelung tiefer gehen, müssen mehrere AnimatorSets verwendet werden oder die Funktion PlaySequentially(). Diese erwartet eine Liste mit Elementen vom Typ Grundtyp Animator (ihr können also einzelne Animationen oder ganze AnimatorSets übergeben werden) und spielt diese dann nacheinander ab.
Möchte man viele Animationen ordnen, ist außerdem die Funktionen PlayTogether() nützlich, welche ebenfalls eine Liste von Animationen erwartet und diese gleichzeitig abspielt.

Samstag, 14. Dezember 2013

Android: Animationen einfügen

Im heutigen Post möchte ich beschreiben, wie man mit Animationen etwas mehr Leben in die eigenen Android Apps hauchen kann.
Bei der Android Programmierung gibt es hierfür im wensentlichen 3 Möglichkeiten, es gibt View Animations, Drawable Animations und Property Animations. In diesem Post möchte ich nur die letzte Klasse abdecken, welche zuletzte eingeführt wurde (in Android 3.0) und daher auch die meisten Möglichkeiten bietet und auch normalerweise genutzt wird.
Bei den Property Animations wiederrum gibt es 3 Subklassen: ValueAnimator, ObjectAnimator und AnimationSet. AnimationSet ist zuständig zum Verbinden mehrerer Animationen, dieser Klasse werde ich demnächst einen eigenen Post widmen.
Mit dem ValueAnimator lassen sich beliebige Eigenschaften über die Zeit verändern. In diesem Post möchte ich jedoch nur die Verwendung der Unterklasse ObjectAnimator beschreiben, welche speziell für das Animieren von Objekten erstellt wurde, was für die meisten Anwendungen ausreichten sollte.
Nun direkt zum Code. Im Beispiel erstellen wir ein LinearLayout, in welchem eine TextView und ein Button untereinander angeordnet werden, und die TextView per Animation nach Klick auf den Button nach rechts bewegt wird.
Zum Animieren brauchen wir den Namespace Android.Animation, welchen wir per using einbinden.
Dann erstellen wir den Animator von der Klasse ObjectAnimator aus und wählen hierbei, ob wir eine Integer oder Float Eigenschaft verändern müssen. Dieser Erzeugungsmethode übergeben wir dann den Namen des zu animierenden Objekts, den Namen der Zieleigenschaft als String und Start- und Endwert. Die folgenden Zeilen ändern die Eigenschaft Left des Objekts TxtView1 von 0 auf 100, verschieben das Objekt also nach rechts. In der zweiten Zeile legen wir die Dauer der Animation in Millisekunden fest, und in der dritten Zeile starten wir schließlich die Animation:

ObjectAnimator Animator = ObjectAnimator.OfInt(TxtView1, "Left", 0, 100);
Animator.SetDuration(1000);
Animator.Start();

Der komplette Code lautet:

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

using Android.Animation;

namespace AnimationsExample
{
     [Activity (Label = "AnimationsExample", MainLauncher = true)]
     public class MainActivity : Activity
     {
          protected override void OnCreate (Bundle bundle)
          {
               base.OnCreate (bundle);

               LinearLayout Layout = new LinearLayout (this);
               Layout.Orientation = Orientation.Vertical;

               TextView TxtView1 = new TextView (this);
               TxtView1.Text = "I'm flying";
               Layout.AddView (TxtView1);

               Button Btn1 = new Button (this);
               Btn1.Text = "Animation";
               Layout.AddView (Btn1);

               Btn1.Click += (object sender, EventArgs e) => {
                    ObjectAnimator Animator = ObjectAnimator.OfInt(TxtView1, "Left", 0, 100);
                    Animator.SetDuration(1000);
                    Animator.Start();
               };

               SetContentView (Layout);
          }
     }
}

Montag, 25. November 2013

Android: App zur Ausgabenübersicht

In diesem Post möchte ich nun mal ein etwas größeres Projekt vorstellen, in welchem alle Tricks aus den vorigen Post benutzt werden.
Das Projekt ist aus meinen eigenen Bedürfnissen entstanden und die resultierende App wird von mir auch fleißig angewendet - es geht um eine App, in welcher sich gemachte Geldausgaben eintragen lassen und übersichtlich anzeigen lassen.
Die App besteht aus 4 Activities: MainActivity.cs und Overview[A - C].cs, sowie 2 Hilfsklassen Tools.cs. und Expenses.cs.
Expenses.cs beschreibt die Klasse Expenses, welche zur Darstellung von Ausgaben benutzt wird. Vorgenommene Ausgaben werden jeweils serialisiert und zur Ausgabendatei des aktuellen Monats hinzugefügt.
Die MainActivity sieht folgendermaßen aus:




















Mit einem Klick auf "Übersicht" wechseln wir zur Activity OverviewA. Hierfür wird die Ausgabenliste zu einem String serialisiert und als Parameter an OverviewA übergeben.
In OverviewA werden alle Aufgaben mit Datum aufgelistet. Die Anzeige ist jeweils pro Monat, der entsprechende Monat kann oben ausgewählt werden.
Wird die Liste zu lang, kann natürlich gescrollt werden.

In OverviewB werden die Ausgaben zu Kategorien zusammengefasst (nach gleichem Namen).




















Schließlich wird in OverviewC der Anteil der Kategorien grafisch mit einem Kreisdiagramm dargestellt.




















Heruntergeladen kann der komplette Quellcode der App hier.

Mehr zum Ausprobieren habe ich (eine verbesserte Version der) App auch in den Google Playstore hochgeladen.

Sonntag, 24. November 2013

Android: ScrollBars einfügen

Angenommen, wir möchten folgendes Layout erzeugen:

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace ScrollbarsExample
{
     [Activity (Label = "ScrollbarsExample", MainLauncher = true)]
     public class MainActivity : Activity
     {
          protected override void OnCreate (Bundle bundle)
          {
               base.OnCreate (bundle);

               var Layout = new LinearLayout (this);
               Layout.Orientation = Orientation.Vertical;

               for (int i=0; i<20; i++) {
                    var TempButton = new Button (this);
                    TempButton.Text = "Button" + i.ToString ();
                    Layout.AddView (TempButton);
               }

               SetContentView (Layout);
          }
     }
}

Also ein Layout, in dem wir 20 Buttons untereinander platzieren. Wahrscheinlich passt dieses nicht komplett auf den Handybildschirm, die App wird dann einfach "abgeschnitten".
Wir können der App aber ganz leicht eine Scrollfunktion hinzufügen, das heißt eine Möglichkeit, dass der Benutzer nach oben oder unten scrollen kann und so das komplette Layout sieht.
Hierfür legen wir quasi als äußerste Instanz ein ScrollView an. Dann fügen wir unser Layout mittels AddView() dem ScrollView hinzu, und setzen das Layout der Applikation auf das ScrollView:


using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace ScrollbarsExample
{
     [Activity (Label = "ScrollbarsExample", MainLauncher = true)]
     public class MainActivity : Activity
     {
          protected override void OnCreate (Bundle bundle)
          {
               base.OnCreate (bundle);

               var MyScrollView = new ScrollView (this);

               var Layout = new LinearLayout (this);
               Layout.Orientation = Orientation.Vertical;

               for (int i=0; i < 20; i++) {
                    var TempButton = new Button (this);
                    TempButton.Text = "Button" + i.ToString ();
                    Layout.AddView (TempButton);
               }

               MyScrollView.AddView (Layout);
               SetContentView (MyScrollView);
          }
     }
}

Nun kann der Benutzer per Fingerbewegung vertikal durch die Applikation steuern.
Ist das Layout horizontal breiter als der Bildschirm, erreichen wir eine horizontale Scrollmöglichkeit durch Verwendung des Steuerelements HorizontalScrollView.

Samstag, 23. November 2013

Android: Aktuelle Activity beenden

In diesem Post möchte ich nur schnell zeigen, wie die aktuelle Activity beendet werden kann.
Dies ist zum Beispiel nützlich, wenn man eine neue Activity starten möchte, und die alte nicht offen im Hintergrund haben möchte.
Der Code lautet:

this.Finish ();

Freitag, 22. November 2013

Android: Zwischen Activities wechseln

Nachdem ich in den vorigen Posts schon ein paar Mal angesprochen habe, dass Android Anwendungen aus Activites aufgebaut sind (genau wie Windows Forms Anwendungen aus Formularen), möchte ich heute zeigen, wie man der Anwendung eine neue Activity hinzufügt, diese aufruft und ihr Daten mitgibt.
Eine neue Activity fügen wir per Datei - Neu - Android Activity hinzu, ich nenne sie hier "SecondActivity.cs". Mit dieser kann nun natürlich genauso verfahren werden wie mit der Hauptactivity.
Es kann also codemäßig ein Layout angelegt werden, oder per axml Datei. Ich wähle hier die zweite Variante, dafür rechtsklicken wir auf Resources - layout und wählen Hinzufügen - Neue Datei - Android Layout. Ich nenne das Layout "Second".
Von der Mainactivity können wir dann die zweite Activity folgendermaßen aufrufen:

var SecondActivity = new Intent(this, typeof(SecondActivity));
StartActivity(SecondActivity);

Optional können wir beim Aufruf Stringparameter übergeben:

SecondActivity.PutExtra("Parameter", "Nachricht von Main");

Der komplette Code der Datei "MainActivity.cs" sieht so aus:

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace MultipleActivities
{
     [Activity (Label = "MultipleActivities", MainLauncher = true)]
     public class MainActivity : Activity
     {
          protected override void OnCreate (Bundle bundle)
          {
               base.OnCreate (bundle);

               var Layout = new LinearLayout (this);
               Layout.Orientation = Orientation.Vertical;
               var Button1 = new Button (this);
               Button1.Text = "Second starten";

               Button1.Click += delegate {
                    var SecondActivity = new Intent(this, typeof(SecondActivity));
                    SecondActivity.PutExtra("Parameter", "Nachricht von Main");
                    StartActivity(SecondActivity);
               };

               Layout.AddView (Button1);

               SetContentView (Layout);
          }
     }
}

Beim Klick auf den Button wird also die zweite Activity gestartet.
Diese enthält nur ein Textview, der dazugehörige Code der Datei "Second.axml" ist:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:minWidth="25px"
    android:minHeight="25px">
    <TextView
        android:text="Medium Text"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView1" />
</LinearLayout>

Der Code der Activity "SecondActivity.cs" ist auch recht kurz, in ihm wird der von der Hauptactivity übergebene Parameter ausgelesen und im Textview dargestellt:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;

namespace MultipleActivities
{
     [Activity (Label = "SecondActivity")]              
     public class SecondActivity : Activity
     {
          protected override void OnCreate (Bundle bundle)
          {
               base.OnCreate (bundle);
               SetContentView (Resource.Layout.Second);

               TextView textView1 = FindViewById (Resource.Id.textView1);
               textView1.Text = Intent.GetStringExtra ("Parameter");
          }
     }
}

Donnerstag, 21. November 2013

Android: App Titel ändern

Im heutigen kurzen Post möchte ich erklären, wie man den Titel einer Android App ändert, genauer gesagt den Titel der einzelnen App Fenster (Activities).
Erstellt man eine neue App, sieht die Hauptactivity folgendermaßen aus, der Titel (rot eingekreist) ist anfänglich der Name des Projekts:









Um diesen zu ändern, ersetzen wir diesen einfach in folgender Zeile, welche sich als Attribut direkt über der Definition der Activity Klasse befindet:

[Activity (Label = "Neuer Titel", MainLauncher = true)]

Per Code lässt sich der Titel über die Eigenschaft Title verändern:

this.Title = "Algo";

Mittwoch, 20. November 2013

Android: Grafik in Layout einfügen

Im vorigen Post habe ich zur Erstellung von Grafiken empfohlen, eine Klasse von View abzuleiten, in dieser das Zeichnen zu erledigen und diese Klasse dann als Layout festzulegen.
Dies führt nun scheinbar zu Problemen bei fortgeschritteneren Apps, wenn man außerdem Steuerelemente etc. auf der Applikation platzieren möchte, da der Befehl SetContentView() nur einmal aufgerufen werden kann. Also entweder mit dem Layout, welches die Steuerelemente beinhaltet, oder mit der eigenen Klasse, welche die Grafiken zeichnet.
Dieses "Problem" lässt sich aber ganz einfach lösen, in dem wir im Steuerelemente Layout Layout die Grafikklasse wie ein Steuerelement mittels AddView() zum Layout hinzufügen, und dann das Layout der Applikation auf Layout setzen.
Ich denke, der folgende Beispielcode sollte nach Lesen der vorangegangenen Posts selbsterklärend sein. Wir legen hier die Klasse ViewWithOval an, welche fast identisch zur Klasse aus dem vorigen Post ist. Sie zeichnet einen Kreis auf das Formular. Der einzige Unterschied ist die öffentliche Variable paint zur Festlegung der Farbe des Kreises.
In der Funktion OnCreate() der Klasse MainActivity legen wir dann ein Layout mit einem Button an. Beim Klick auf diesen Button ändern wir die Farbe des Kreises und erzwingen das Neuzeichnen mittels Invalidate(). Dann fügen wir den Kreis dem Layout hinzu und legen das Layout der Applikation fest:

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace CombinedLayouts
{
     [Activity (Label = "CombinedLayouts", MainLauncher = true)]
     public class MainActivity : Activity
     {

          protected override void OnCreate (Bundle bundle)
          {
               base.OnCreate (bundle);

               var Layout = new LinearLayout (this);
               Layout.Orientation = Orientation.Vertical;

               var Button1 = new Button(this);
               Layout.AddView (Button1);

               ViewWithOval OvalGraphic = new ViewWithOval (this);
              
               Button1.Click += delegate {
                    OvalGraphic.paint.SetARGB(255, 255, 0, 0);
                    OvalGraphic.Invalidate();
               };

               Layout.AddView (OvalGraphic);

               SetContentView (Layout);
          }
     }

     public class ViewWithOval : View
     {
          public Android.Graphics.Paint paint = new Android.Graphics.Paint ();
          private readonly Android.Graphics.Drawables.ShapeDrawable Oval;

          public ViewWithOval(Context context) : base (context)
          {         
               paint.SetARGB(255, 0, 255, 0);

               Oval = new Android.Graphics.Drawables.ShapeDrawable(new Android.Graphics.Drawables.Shapes.OvalShape());
               Oval.Paint.Set(paint);

               Oval.SetBounds(0, 0, 500, 500);
          }

          protected override void OnDraw(Android.Graphics.Canvas canvas)
          {
               Oval.Paint.Set(paint);
               Oval.Draw(canvas);
          }
     }
}


Dienstag, 19. November 2013

Android: Grafiken

Bei der Android App Programmierung gibt es im wesentlichen zwei Möglichkeiten, um 2D Grafiken zu erzeugen: Dies geht zum einen mit sogenannten DrawableResources oder durch die Verwendung eines Canvas Objekts.
DrawableResources sind geometrische Formen, die in einer XML Datei definiert sind und dann auf der App angezeigt werden können.
Auf ein Canvas Objekt kann dagegen beliebig gezeichnet werden, ähnlich wie in System.Graphics.

Zuerst ein kurzes Beispiel zu DrawableResources: Wir klicken rechts auf den Order Resources - Drawable und wählen Datei hinzufügen - XML Datei aus. In dieser fügen wir dann den folgenden XML Code ein:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="line">
    <stroke android:width="1dp" android:color="#FFFFFF"/>
    <size android:height="50dp" />
</shape>

Wir definieren also, dass die Datei  eine Figur (shape) beschreibt, und legen fest, dass wir eine Linie darstellen möchten. Diese soll 1dp breit sein und 50dp lang, außerdem schwarze Farbe haben. Dp steht für Density-independent Pixels und ist eine Maßeinheit, welche sich der physikalischen Bildschirmgröße anpasst. Die definierte Figur kann dann z.B. als Hintergrund für ein Steuerelement verwendet werden (bei mir heißt die XML Datei Line.xml):

var TxtLine = new TextView (this);
TxtLine.SetBackgroundResource(Resource.Drawable.Line);

Natürlich stehen noch weitere Figuren außer Linie zur Verfügung, auf dieser Seite von Xamarin gibt es ein weiteres Beispiel.

Nun möchte ich mich etwas mehr der zweiten vorgestellten Methode widmen. Eine oft benutze Technik ist die Erstellung einer eigenen View Klasse, in der das Zeichnen erledigt wird, und die dann als Layout für die Anwendung festgelegt wird.
Ich werde hier das Zeichnen einer Ellipse zeigen, aufbauend auf diesem Tutorial von Xamarin.
Ich poste einfach mal den kompletten Code der Klasse:

     public class ViewWithOval : View
     {
          private readonly ShapeDrawable Oval;

          public ViewWithOval(Context context) : base (context)
          {
               var paint = new Paint();
               paint.SetARGB(255, 0, 255, 0);

               Oval = new ShapeDrawable(new OvalShape());
               Oval.Paint.Set(paint);

               Oval.SetBounds(0, 0, 500, 500);
          }

          protected override void OnDraw(Canvas canvas)
          {
               Oval.Draw(canvas);
          }
     }

Wir legen also eine Klasse namens ViewWithOval an, und leiten sie von der Basisklasse View ab. Darin legen wir eine Variable vom Typ ShapeDrawable an, in welcher wir eine beliebige Zeichenfigur speichern können. Im Konstruktor der Klasse legen wir fest, dass es eine Ellipse werden soll. Außerdem setzen wir die Farbe dieser auf Grün. Mittels SetBounds() legen wir den Zeichenbereich fest, welcher von der Ellipse voll ausgenutzt werden wird. Der quadratische Bereich hier führt also zum Zeichnen eines Kreises.
Wichtig ist die Überladung der Funktion OnDraw(), welche beim Neuzeichnen der Klasse aufgerufen wird, hier sorgen wir dafür, dass die Figur gezeichnet wird.
Hier könnten zum Beispiel auch andere Objekte gezeichnet werden, z.B. über canvas.DrawText("Ich zeichne einen String", ...) eine Zeichenfolge.
In unserer MainActivity Klasse müssen wir nun nur noch das Layout auf diese Klasse festlegen, was wir in der OnCreate() Funktion folgendem Befehl machen:

SetContentView (new ViewWithOval(this));

Das Ergebnis sieht auf meinem Handy so aus:


Montag, 18. November 2013

Android: Layouting

Heute möchte etwas mehr auf das Thema Layouting für Android Apps eingehen und insbesondere die Verwendung des TableLayouts zeigen, mit dem sich schon eine Menge machen lässt.
In den vorigen beiden Posts haben wir eine erste Android Anwendung erstellt und darin Steuerelemente mit einem LinearLayout untereinander angeordnet.
Mit einem TableLayout lassen sich, wie der Name schon sagt, diese in Tabellenform anordnen, sodass quasi beliebige Positionierungen möglich sind.
Zuerst werde ich die Erstellung des Layouts per AXML Datei beschreiben, wie im vorigen Post erklärt, und am Ende den C# Code zeigen, welcher zum gleichen Ergebnis führt.
In der Datei "Main.axml" ändern wir zuerst das Layout von Linear zu Table:

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

Ein TableLayout besteht aus TableRows (Zeilen). In jeder Zeile können wir Steuerelemente platzieren, welche dann nebeneinander angeordnet werden.
Folgendermaßen fügen wir dem Layout eine neue Zeile hinzu und dieser ein Textview Steuerelement:

<TableRow>
    <TextView
        android:text="Textview1"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textView1" />
</TableRow>

Durch Hinzufügen einer leeren TableRow können wir einen vertikalen Abstand einfügen. Darunter legen wir eine neue Zeile mit einem Button und einer Textview an:

<TableRow>
</TableRow>
<TableRow>
    <Button
        android:id="@+id/button1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Button1" />
    <TextView
        android:text="Textview2"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textView2" />
</TableRow>

Das entstandene Layout sieht so aus:










Die einzelnen Tabellenzellen passen sich also von der horizontalen Ausrichtung und Größe her an, das heißt, die Zelle, die Button1 beinhaltet, hat die gleiche Größe wie die Zelle von Textview1. Wird diese z.B. größer weil sich der enthaltene Text verlängert, wird auch die Zelle von Button1 größer, Textview2 schiebt sich nach rechts.
Möchte man ein davon losgelöstes Steuerelement erstellen, zum Beispiel eine Überschrift, welche nicht die Größe der folgenden Zellen beeinflusst, deklariert man dieses ohne umfassende TableRow.
Hier der komplette axml Code, mit einer weiteren Textview, welches eine lange Überschrift am oberen Bildschirmrand enthält:

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:text="Looooong Headline"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textView3" />
    <TableRow>
        <TextView
            android:text="Textview1"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/textView1" />
    </TableRow>
    <TableRow>
    </TableRow>
    <TableRow>
        <Button
            android:id="@+id/button1"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Button1" />
        <TextView
            android:text="Textview2"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/textView2" />
    </TableRow>
</TableLayout>

Nun noch wie angekündigt, der Inhalt der Datei "MainActivity.cs", welcher alternativ zur oben genannten Möglichkeit der Definition per axml das gleiche Resultat erzeugt:

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace TableLayoutExample
{
     [Activity (Label = "TableLayout", MainLauncher = true)]
     public class MainActivity : Activity
     {
          protected override void OnCreate (Bundle bundle)
          {
               base.OnCreate (bundle);

               var Layout = new TableLayout (this);
               Layout.Orientation = Orientation.Vertical;

               var Textview3 = new TextView (this);
               Textview3.Text = "Looooong Headline";
               Layout.AddView (Textview3);

               TableRow TableRow1 = new TableRow (this);
               var Textview1 = new TextView (this);
               Textview1.Text = "Textview1";
               TableRow1.AddView (Textview1);
               Layout.AddView (TableRow1);

               TableRow TableRow2 = new TableRow (this);
               Layout.AddView (TableRow2);

               TableRow TableRow3 = new TableRow (this);
               var Button1 = new Button (this);
               Button1.Text = "Button1";
               var Textview2 = new TextView (this);
               Textview2.Text = "Textview2";
               TableRow3.AddView (Button1);
               TableRow3.AddView (Textview2);
               Layout.AddView (TableRow3);

               SetContentView (Layout);
          }
     }
}


Sonntag, 17. November 2013

Android: Layout per AXML definieren

Im vorigen Post habe ich die Erstellung einer ersten Android App beschrieben. Dort haben wir 2 Steuerelemente auf dem App Fenster angelegt, ein Textview und einen Button, und diese mittels einem LinearLayout untereinander angeordnet. Die Erzeugung der Steuerelemente und ihre Platzierung haben wir im Code der OnCreate() Funktion vorgenommen.
Doch wie auch bei WPF Anwendungen, kann bei Android Apps das Layout über eine auf XML basierende Datei festgelegt werden. Im heutigen Post möchte ich darüber schreiben und das Layout der App aus dem vorigen Post mit diesem Prinzip anlegen.
Xamarin speichert das Layout als sogenannte axml Datei. Bei Anlegen des Projekts wird für die Hauptactivity direkt eine dazugehörige Layout Datei angelegt. Diese findet sich bei Expansion des Ordners "Resources" auf der linken Seite, und dann "layout" unter dem Namen "main.axml".
Klickt man diese an, öffnet sich die Datei, entweder können nun wie im Visual Studio Steuerelemente per Drag & Drop auf das Fenster gezogen werden oder nach Klick auf "Quelle" per AXML Code angelegt werden. Ich werde hier die zweite Variante beschreiben.
Die ersten Zeilen der Datei sehen so aus:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

Hier wird unter anderem die verwendete XML Version klassifiziert, und, für uns wichtig, schon der Typ des Layouts festgelegt, nämlich das von uns gewünschte LinearLayout.
Wir fügen nun mit ein paar Zeilen XML Code, welcher selbsterklärend sein sollte, die Steuerelemente hinzu:

<TextView
android:text="Hello Android"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/TextView1" />
<Button
    android:id="@+id/Button1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Click me" />

Nach einem Klick auf "Inhalt", wird eine Vorschau der Programmoberfläche angezeigt:
Der komplette XML Code der Datei Main.axml lautet dann:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
     android:text="Hello Android"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/TextView1" />
    <Button
        android:id="@+id/Button1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Click me" />
</LinearLayout>

Zurück im C# Code der Datei "MainActivity.cs" müssen wir dem Programm nun zuerst mitteilen, das Layout aus den Ressourcen zu nehmen, welches wir mit
SetContentView (Resource.Layout.Main);

erledigen. Über Resource.Layout greifen wir auf den entsprechenden Ressourcenordner zu, Main(.axml) ist der Name unserer gewünschten Datei.
Um auf die Steuerelemente zugreifen zu können, müssen wir sie anhand ihrer ID referenzieren:

Button Button1 = FindViewById<button> (Resource.Id.Button1);
TextView TextView1 = FindViewById (Resource.Id.TextView1);

Der restliche Code zur Hinzufügung des Ereignishandlers ist natürlich gleich, der komplette Code der Datei "MainActivity.cs" lautet:

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace FirstApplicationAXML
{
     [Activity (Label = "FirstApplicationAXML", MainLauncher = true)]
     public class MainActivity : Activity
     {
          protected override void OnCreate (Bundle bundle)
          {
               base.OnCreate (bundle);

               SetContentView (Resource.Layout.Main);

               Button Button1 = FindViewById<button> (Resource.Id.Button1);
               TextView TextView1 = FindViewById (Resource.Id.TextView1);

               Button1.Click += delegate {
                    TextView1.Text = "Button clicked";
               };
          }
     }
}

Samstag, 16. November 2013

Android: Eine erste App erstellen

Heute geht es um die Erstellung einer ersten Android App mittels Xamarin.
Dazu öffnen wir das Xamarin Studio und klicken auf Datei - Neue Projektmappe - C# - Android Application.
Was zum Beispiel bei Windows Forms Anwendungen die Formulare sind, sind bei Android Activities, welche ein Fenster der Anwendung repräsentieren. Deswegen ist für uns die Datei "MainActivity.cs" wichtig, welche den Code für das Hauptfenster (und bisher einzige) der Anwendung enthält.
Wir sehen, dass die Activity wie in Windows Forms Anwendungen durch eine Klasse (MainActivity) dargestellt wird. Nach Erstellen des Projekts enthält diese bereits die Funktion OnCreate(). Diese wird aufgerufen, wenn die App gestartet wird. Zu Demozwecken ist die Funktion bereits mit Code gefüllt, den wir aber löschen, bis auf die erste Zeile (base.OnCreate (bundle);).
Wir möchten nun eine einfache Anwendung mit einem Textfeld und einem Button erstellen.
Dazu müssen wir zuerst ein Layout anlegen, um festzulegen, wie die Steuerelemente auf dem Fenster angeordnet werden sollen. Wir nehmen hier ein LinearLayout, bei diesem Layout werden die Steuerelemente einfach nebeneinander oder untereinander angeordnet:

var Layout = new LinearLayout (this);
Layout.Orientation = Orientation.Vertical;

Ein Layout sowie Steuerelemente erwarten im Konstruktor einen sogenannten Kontext, hier übergeben wir this stellvertretend für die aktuelle Activity.
Nun legen wir die Steuerelemente per Code an, ein Label heißt hier Textview:

var TextView1 = new TextView (this);
TextView1.Text = "Hello Android";
Layout.AddView (TextView1);

Wie ihr sehen könnt, fügen wir in der letzten Zeile das Steuerelement dem Layout hinzu.
Das Hinzufügen das Buttons läuft analog ab, nur dass wir mit einer anonymen Methode eine Ereignisbehandlung für das Click Ereignis einrichten:

var Button1 = new Button (this);
Button1.Text = "Click me";
Button1.Click += delegate {
     TextView1.Text = "Button clicked";
};
Layout.AddView (Button1);

Am Ende müssen wir der Activity nur noch sagen, welches Layout sie verwenden soll:

SetContentView (Layout);

Und damit ist die App bereit zur Ausführung. Noch einmal der komplette Code:

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace FirstApplication
{
     [Activity (Label = "FirstApplication", MainLauncher = true)]
     public class MainActivity : Activity
     {
          protected override void OnCreate (Bundle bundle)
          {
               base.OnCreate (bundle);

               var Layout = new LinearLayout (this);
               Layout.Orientation = Orientation.Vertical;

               var TextView1 = new TextView (this);
               TextView1.Text = "Hello Android";
               Layout.AddView (TextView1);

               var Button1 = new Button (this);
               Button1.Text = "Click me";
               Button1.Click += delegate {
                    TextView1.Text = "Button clicked";
               };
               Layout.AddView (Button1);

               SetContentView (Layout);
          }
     }
}

Starten lässt sich die App dann per Ausführen - (Ohne) Debugger starten. Dazu muss entweder ein angeschlossenes Handy oder ein Emulator ausgewählt werden. Ich habe keine Emulatoren eingerichtet, sondern teste immer auf meinem Handy. Xamarin lädt die App dann aufs Handy, wo sie automatisch ausgeführt wird.
Um die App "per Hand" auf ein Handy zu kopieren, muss sie als Release Version kompiliert werden (Projekt - Aktive Konfiguration - Release), dann kann die Datei "AppName.AppName-Signed.apk" aus dem Release Ordner ins gewünschte Verzeichnis kopiert werden. Android führt nämlich nur signierte Anwendungen aus, was von Xamarin in der "Signed" Datei schon vorgenommen wurde. Falls die App aber im weiteren Kreis veröffentlicht werden soll (z.B. auf Google Play), sollte (muss) sie aber persönlich signiert werden.

Freitag, 15. November 2013

Xamarin installieren

In diesem Post möchte ich kurz die Installation und Einrichtung des Xamarin Studios zeigen, welches für die App Programmierung für Android und iOS benötigt wird.
Heruntergeladen werden kann Xamarin unter diesem Link. Dann einfach den Anweisungen folgen.
Wie im vorigen Post schon erwähnt, beschränke ich mich auf diesem Blog auf die Entwicklung für Android.
Hier gibt es eine Anleitung von Xamarin zur Installation sowie Hinweise zur Einrichtung der Entwicklungsumgebung. Ich musste jedoch nur 2 Schritte davon durchführen, nämlich die Installation der USB Treiber und das Herstellen des Debug Modus auf dem Handy.
Es sind spezielle Treiber notwendig, damit man per USB seine Anwendungen (zu Debug Zwecken) auf das Handy übertragen kann.
Beide Schritte sind auf der verlinkten Seite beschrieben, dort findet sich auch eine Liste mit den passenden USB Treibern für verschiedene Hersteller. Bei meinem Samsung S3 reichte die Installation des Wartungsprogramm "Kies" aus (ich weiß nicht, ob es sogar auch ohne Installation dieses geklappt hätte).
Und schon ist euer Computer und Handy bereit für die App Entwicklung!
Ein kleines Manko: Angeblich soll Xamarin automatisch in die .Net Entwicklung integriert werden, was bei mir nicht der Fall war. Doch mit dem Xamarin Studio komme ich gut zurecht, es ist wahrscheinlich sogar besser für die App Entwicklung.

Donnerstag, 14. November 2013

App Programmierung mit C#

Wahrscheinlich kennen die meisten Java als Entwicklungsspache für Android und das Apple eigene Studio für iOS - allerdings geht dies auch mit C#!
In der nächsten Postreihe möchte ich euch deshalb zeigen, wie man Android und iOS Apps mittels C# entwickelt.
Kernstück hierfür ist das Xamarin Studio, welches auf Mono basiert. Mono ist eine plattformübergreifende Implementierung des .Net Frameworks für u.A. Linux, und jetzt eben auch Android und iOS.
Mit Hilfe des Xamarin Studios können Apps mit herkömmlichen C# Code geschrieben werden, es wird also natürlich die gewohnte C# Syntax benutzt, außerdem können alle nicht an Windows gebundenen Funktionen und Bibliotheken benutzt werden, wie Streams, Serialisierung, Dateizugriff etc.
Der Compiler übersetzt den Code dann in das richtige Format für Android oder iOS.
Da ich leider nur ein Android Handy habe, werde ich mich (momentan) auf diesem Blog auf Android beschränken.
Die Posts dazu werde ich teilweise mit dem Präfix "Android:" im Titel kennzeichnen, um jegliche Verwechselung mit Tipps zu Windows C# auch trotz Labels zu vermeiden.
Ein Manko hat Xamarin jedoch, die kostenfreie Version erlaubt nur Apps bis zu einer bestimmten Dateigröße und verbietet außerdem das Einbinden von externen Java Bibliotheken, die zum Beispiel zur Anzeige von Werbung nötig wären.

Montag, 11. November 2013

WPF Tutorial Teil 4 - Databinding

Ein weiteres nettes Feature von WPF ist Databinding. Damit wird die Möglichkeit beschrieben, die grafische Benutzeroberfläche direkt mit den zu Grunde liegenden Daten zu verbinden - zum Beispiel eine Textbox mit einer Stringvariablen einer Klasse. Je nach Einstellung wird dann eine Änderung des Strings direkt in der Textbox angezeigt und / oder umgekehrt eine Änderung übernommen.

Im heutigen Post möchte ich dazu ein Beispiel vorstellen, welches Daten eines Mitarbeiters verwaltet. Dieser wird durch die Klasse Employee beschrieben, gespeichert werden Vorname, Nachname, Gehalt und Status.
Beim Databinding wird nun immer ein Formular, ein Steuerlement o.ä. mit einer Instanz einer Klasse verknüpft. Hier verknüpfen wir das ganze Formular mit einer Instanz der Klasse Employees, dann können verschiedene Steuerelemente dadrauf die verschiedenen Eigenschaften der Klasse darstellen.
Nach dem Anlegen einer Instanz namens Employee1 legen wir die Datenquelle des Formulars folgendermaßen fest (z.B. im Konstruktor):

this.DataContext = Employee1;

Die Verknüpfung der einzelnen Steuerelemente mit den Variablen des Angestellten können wir nun in der XAML Datei vornehmen, beispielsweise so:

<TextBox Name="txtName" Grid.Row="0" Grid.Column="0" Height="25" Text="{Binding Path=LastName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></TextBox>

Wie man sieht, wird der Eigenschaft Text des Steuerelements kein direkter Text zugewiesen, sondern es wird ein Binding angelegt. Path beschreibt den Pfad des Bindings. Da wir für das gesamte Formular Employee1 als Datenquelle festgelegt haben, wird die Textbox hier mit der Variablen LastName von Employee1 verknüpft.
Die Eigenschaft Mode gibt an, wie Änderungen übernommen werden sollen. TwoWay bedeutet, dass Änderungen in der Textbox an die verbundene Variable weitergeleitet werden sollen, und Änderungen in dieser an die Textbox weitergeleitet werden. Weitere Möglichkeiten sind OneWay (Änderung in der Variablen wird in der Textbox angezeigt) und OneWayToSource (Änderung in der Textbox wird an die Variable weitergeleitet).
Die Eigenschaft UpdateSourceTrigger gibt an, wann die Bindung aktiv wird. Im Falle von Explicit werden Änderungen nur propagiert, wenn die Methode UpdateSource() aufgerufen wird. LostFocus gibt an, dass Änderungen übernommen werden, wenn das verbundene Steuerelement den Fokus verliert, und das hier verwendete PropertyChanged drückt aus, dass Änderungen unverzüglich bei Änderungen weitergeleitet werden.

Voraussetzung dafür, dass Databinding verwendet werden kann, ist die Benutzung von Getter und Setter. Bei der Variablen LastName kann das zum Beispiel so aussehen:

private string lastName;
public string LastName
{
    get { return lastName; }
    set
    {
        lastName = value;
    }
}

Die "eigentliche" Variable ist hier lastName. Diese ist aber hinter dem öffentlichen Feld LastName "versteckt", welches beim Abrufen über get den Wert von lastName zurückgibt, oder diesen setzt. Der Vorteil von einer solchen Vorgehensweise ist die Möglichkeit zur Überprüfung und Wahrung der Datenkonsistenz.

Führt man den Code nun so aus, erkennt man ein Problem: Änderungen in der Textbox werden zwar direkt an die Variable weitergegeben, das heißt, sie speichert den neuen Wert, allerdings führen Änderungen in der Variablen nicht zu Änderungen in der Textbox. Dieses müssen wir manuell auslösen. Dafür muss zuerst die Klasse Employee von INotifyPropertyChanged erben. Anschließend erstellen wir eine Benachrichtigungsfunktion:

        public event PropertyChangedEventHandler PropertyChanged;

        private void Notify(string argument)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(argument));
            }
        }

Im Setter der gewünschten Variablen rufen wir dann diese Funktion auf, z.B. also Notify("LastName").
Nun ist das Databinding in beide Richtungen funktionstüchtig.

Eine Sache die ich noch ansprechen möchte ist DataConversion. Im Code ist die Eigenschaft Salary vom Typ int vorhanden und Position vom Typ einer eigenen Aufzählung. Beim Databinding wird dieses automatisch in einen String umgewandelt, was auch kein Problem ist, da .Net Methoden zur Umwandlung kennt. Was aber, wenn man z.B. eine eigene Klasse geschrieben hat und diese in einen String umwandeln möchte? Dann muss man einen Converter schreiben. Aber diesen kann man auch so benutzen, um z.B. die Umwandlung zu personalisieren.
Ich tue dies hier bei der Umwandlung der Aufzählung. Diese enthält 3 Typen: Undefined, Trainee, Manager. Auch wenn Employee1 noch nichts zugewiesen wurde, zeigt das für Position zuständige Textfeld Undefined an (das gleiche gilt für Gehalt: 0). Dies ändern wir nun, so dass bei der Konvertierung "" zurückgegeben wird wenn die Variable auf Undefined steht.
Dafür brauchen wir eine Converter Klasse, welche wir uns selber schreiben und die wie folgt aussieht:

    [ValueConversion(typeof(Employee.Positions), typeof(string))]
    public class EmployeePositionsConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if ((int)value == 0)
                return "";
            else
                return value.ToString();
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            switch ((string)value)
            {
                case "Trainee":
                    return Employee.Positions.Trainee;
                case "Manager":
                    return Employee.Positions.Trainee;
                default:
                    return Employee.Positions.Undefined;
            }
        }
    }

Über dem Namen geben wir an, zwischen welchen 2 Typen konvertiert wird. Die Funktion Convert() ist dann für die Konvertierung von Employee.Positions nach String zuständig, ConvertBack() für die Rückrichtung. Ich denke die Funktionsweise ist klar.
Wir müssen dem Programm nun jedoch noch sagen,den Converter zu benutzen. Dazu legen wir uns in der XAML Datei im Window Tag zuerst ein Attribute "c" an, welches unseren Namespace speichert:

xmlns:c="clr-namespace:WPFDatabinding"

Dann fügen wir dem Projekt eine Ressource hinzu:

    <Window.Resources>

        <c:EmployeePositionsConverter x:Key="EmployeePositionsConverter"/>
    </Window.Resources>


Die Bedeutung dieses Codes ist folgende: Da in "c" unser Namespace gespeichert ist, greifen wir mit c:EmployeePositionsConverter auf unseren Converter zu und machen ihn unter dem Namen EmployeePositionsConverter zugreifbar.
In der Definition der Textbox können wir ihn dann einfach verwenden:

<TextBox Name="txtPosition" Grid.Row="3" Grid.Column="0" Height="25" Text="{Binding Path=Position, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Converter={StaticResource EmployeePositionsConverter}}"></TextBox>

Nun der komplette Code des Programs - es sind 4 Textfelder und 2 Buttons enthalten. Die Textfelder stellen wie beschrieben die Eigenschaften von Employee1 dar. Bei Klick auf Button 1 wird Employee1 mit Daten gefüllt (er ist dann Hans Wurst), die Textfelder ändern sich entsprechend. In die Textfelder können aber auch neue Daten eingegeben werden, bei Klick auf Button 2 werden die in Employee1 gespeicherten Daten ausgegeben.

MainWindow.xaml:


<Window x:Class="WPFDatabinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:c="clr-namespace:WPFDatabinding"

        Title="MainWindow" Height="350" Width="525"
        >

    <Window.Resources>
        <c:EmployeePositionsConverter x:Key="EmployeePositionsConverter"/>
    </Window.Resources>

    <Grid Height="160" Width="200" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="15">
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>
        <TextBox Name="txtName" Grid.Row="0" Grid.Column="0" Height="25" Text="{Binding Path=LastName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></TextBox>
        <TextBox Name="txtFirstName" Grid.Row="1" Grid.Column="0" Height="25" Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></TextBox>
        <TextBox Name="txtSalary" Grid.Row="2" Grid.Column="0" Height="25" Text="{Binding Path=Salary, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></TextBox>
        <TextBox Name="txtPosition" Grid.Row="3" Grid.Column="0" Height="25" Text="{Binding Path=Position, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Converter={StaticResource EmployeePositionsConverter}}"></TextBox>
        <Button Name="btnNew" Grid.Row="4" Grid.Column="0" Width="100" Height="25" Click="btnNew_Click" Margin="-80, 0, 0,0">Hans Wurst</Button>
        <Button Name="btnCheck" Grid.Row="4" Grid.Column="0" Width="50" Height="25" Click="btnCheck_Click"  Margin="80, 0, 0,0">Check</Button>
    </Grid>
</Window>

MainWindow.xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.ComponentModel;

namespace WPFDatabinding
{
   ///





   /// Interaktionslogik für MainWindow.xaml
   ///

    public partial class MainWindow : Window
    {
        Employee Employee1 = new Employee();
        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = Employee1;
        }

        private void btnNew_Click(object sender, RoutedEventArgs e)
        {
            Employee1.LastName = "Wurst";
            Employee1.FirstName = "Hans";
            Employee1.Salary = 1000;
            Employee1.Position = Employee.Positions.Trainee;
        }

        private void btnCheck_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(Employee1.LastName + Environment.NewLine + Employee1.FirstName + Environment.NewLine + Employee1.Salary.ToString() + Environment.NewLine + Employee1.Position.ToString());
        }
    }

    [ValueConversion(typeof(Employee.Positions), typeof(string))]
    public class EmployeePositionsConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if ((int)value == 0)
                return "";
            else
                return value.ToString();
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            switch ((string)value)
            {
                case "Trainee":
                    return Employee.Positions.Trainee;
                case "Manager":
                    return Employee.Positions.Trainee;
                default:
                    return Employee.Positions.Undefined;
            }
        }
    }

    class Employee : INotifyPropertyChanged
    {
        public enum Positions {Undefined, Manager, Trainee}
        public event PropertyChangedEventHandler PropertyChanged;

        private void Notify(string argument)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(argument));
            }
        }

        private string firstName;
        private string lastName;
        private int salary;
        private Positions position;

        public string FirstName
        {
            get { return firstName; }
            set
            {
                firstName = value;
                Notify("FirstName");
            }
        }

        public string LastName
        {
            get { return lastName; }
            set
            {
                lastName = value;
                Notify("LastName");
            }
        }

        public int Salary
        {
            get { return salary; }
            set
            {
                salary = value;
                Notify("Salary");
            }
        }

        public Positions Position
        {
            get { return position; }
            set
            {
                position = value;
                Notify("Position");
            }
        }
    }
}


Auf Udo's Blog gibt es ebenfalls einen Post zu diesem Thema.