Sunday, November 16, 2014

Arduino MP3-Player (6) - Mehrere Knöpfe (digital)

Der Aufbau der Schaltung für mehrere Knöpfe ist nicht sonderlich anders als für einen Knopf. Es können sowohl digitale als auch analoge Pins verwendet werden. Hier sind 5 digitale und 5 analoge Pins verwendet worden. Alle werden als digitale Eingänge verwendet.


Der Programmcode ist leicht verändert worden. Zum einen wird die LED nicht mehr verwendet. Dadurch ist die Logik des Programmcodes vereinfacht worden. Zum anderen werden Arrays verwendet, deren Inhalt über eine Schleife abgearbeitet wird. Dadurch konnten Codezeilen gespart werden, im Vergleich zum Ausprogrammieren der Logik für jeden einzelnen Pin.

// constants won't change
 
// the number of the pushbutton pins
const int buttonPins[] = { 2, 5, 8, 9, 10, A0, A1, A2, A3, A4 };
 
// variables will change
 
// variable for reading the pushbutton status
int buttonStates[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 
// variable for remember the number of button pressed
int counters[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 
 
// the setup routine runs once when you turn the device on or you press reset
void setup()
{
    // initialize the pushbutton pins as input and enable internal pull-up resistor
    for (int i = 0; i < (sizeof(buttonPins) / sizeof(int)); i++)
    {
        pinMode(buttonPins[i], INPUT_PULLUP);
    }
 
    // disable LED L
    pinMode(13, OUTPUT);
    digitalWrite(13, LOW);
 
    // initialize serial communication at 9600 bits per second
    Serial.begin(9600);
}
 
 
// the loop routine runs over and over again forever
void loop()
{
    int state;
 
    // go through all button pins
    for (int i = 0; i < 10; i++)
    {
        // read the state of the pushbutton value
        state = digitalRead(buttonPins[i]);
        
        // recognize state changes: button pressed and button released
        if (state != buttonStates[i])
        {
            // remember new button state
            buttonStates[i] = state;
 
            // print out the state of the button
            Serial.print(buttonPins[i]);
            Serial.print(" State changed ");
            Serial.println(buttonStates[i]);
 
            // button is pressed
            if (buttonStates[i] == LOW)
            {
                // increment number of button pressed
                counters[i]++;
 
                // print out the number of button pressed
                Serial.print(buttonPins[i]);
                Serial.print(" counter: ");
                Serial.println(counters[i]);
            }
            // button is released
            else
            {
                // print out new line
                Serial.println();
 
                // wait before next click is recognized
                delay(100);
            }
        }
    }
}

Nächstes Mal werde ich mir eine Alternative für "Mehrere Knöpfe" mit analogen Pins anschauen.

Saturday, November 15, 2014

Arduino MP3-Player (5) - Knöpfe (digital)

Knopfdrücke können im Arduino registriert werden. Nach einem Knopfdruck können dann entsprechende Aktionen ausgeführt werden. Ein einfacher Schaltkreis kann realisiert werden indem der Knopf mit dem digitalen Port 2 und der Masse verbunden wird. Dabei wird der interne Pull-up Widerstand verwendet.


Der Programmcode schaltet solange der Knopf gedrückt wird die LED an. Wird der Knopf wieder losgelassen, wird die LED wieder ausgeschaltet. Jeder Knopfdruck wird gezählt. Der aktuelle Status des Knopfs und die Anzahl der Knopfdrücke wird über eine serielle Kommunikation ausgegeben.

// constants won't change
const int buttonPin = 2; // the number of the pushbutton pin
const int ledPin = 13;   // the number of the LED pin
 
// variables will change
int buttonState = 0;     // variable for reading the pushbutton status
 
int counter = 0;         // variable for remember the number of button pressed
 
 
// the setup routine runs once when you turn the device on or you press reset
void setup()
{
    // initialize the LED pin as an output
    pinMode(ledPin, OUTPUT);
 
    // initialize the pushbutton pins as input and enable internal pull-up resistor
    pinMode(buttonPin, INPUT_PULLUP);
 
    // initialize serial communication at 9600 bits per second
    Serial.begin(9600);
}
 
 
// the loop routine runs over and over again forever
void loop()
{
    // read the state of the pushbutton value
    int state = digitalRead(buttonPin);
 
    // recognize state changes: button pressed and button released
    if (state != buttonState)
    {
        // remember new button state
        buttonState = state;
 
        // print out the state of the button
        Serial.print("2 State changed ");
        Serial.println(buttonState);
 
        // button is pressed
        if (buttonState == LOW)
        {
            // turn LED on
            digitalWrite(ledPin, HIGH);
 
            // increment number of button pressed
            counter++;
 
            // print out the number of button pressed
            Serial.print("2 counter: ");
            Serial.println(counter);
        }
        // button is released
        else if (buttonState == HIGH)
        {
            // turn LED off
            digitalWrite(ledPin, LOW);
        }
    }
}

Dieser Programmcode kann für Aktionen verwendet werden, die ausgeführt werden sollen, solange ein Knopf gedrückt ist. Eine Alternative zu dieser Logik ist es, dass eine Aktion bei einem Knopfdruck nur einmal ausgeführt werden soll. Dazu das folgende Beispiel. Der Programmcode schaltet bei einem Knopfdruck die LED an, bei einem weiteren Knopfdruck wieder aus. Jeder Knopfdruck wird gezählt. Der aktuelle Status des Knopfs, der LED und die Anzahl der Knopfdrücke wird über eine serielle Kommunikation ausgegeben.

// constants won't change
const int buttonPin = 2; // the number of the pushbutton pin
const int ledPin = 13;   // the number of the LED pin
 
// variables will change
int buttonState = 0;     // variable for reading the pushbutton status
int ledState = 0;        // variable for reading the LED status
 
int counter = 0;         // variable for remember the number of button pressed
 
 
// the setup routine runs once when you turn the device on or you press reset
void setup()
{
    // initialize the LED pin as an output
    pinMode(ledPin, OUTPUT);
 
    // initialize the pushbutton pins as input and enable internal pull-up resistor
    pinMode(buttonPin, INPUT_PULLUP);
 
    // initialize serial communication at 9600 bits per second
    Serial.begin(9600);
}
 
 
// the loop routine runs over and over again forever
void loop()
{ 
    // read the state of the pushbutton value
    int state = digitalRead(buttonPin);
 
    // recognize state changes: button pressed and button released
    if (state != buttonState)
    {
        // remember new button state
        buttonState = state;
 
        // print out the state of the button
        Serial.print("2 State changed ");
        Serial.println(buttonState);
 
        // button is pressed and LED is off
        if (buttonState == LOW && ledState == LOW)
        {
            // remember new LED state
            ledState = HIGH;
 
            // turn LED on
            digitalWrite(ledPin, HIGH);
 
            // print out the state of the LED
            Serial.print("2 Set high ");
            Serial.println(ledState);
 
            // increment number of button pressed
            counter++;
 
            // print out the number of button pressed
            Serial.print("2 counter: ");
            Serial.println(counter);
        }
        // button is pressed and LED is on
        else if (buttonState == LOW && ledState == HIGH)
        {
            // remember new LED state
            ledState = LOW;
 
            // turn LED off
            digitalWrite(ledPin, LOW);
 
            // print out the state of the LED
            Serial.print("2 Set low ");
            Serial.println(ledState);
 
            // increment number of button pressed
            counter++;
 
            // print out the number of button pressed
            Serial.print("2 counter: ");
            Serial.println(counter);
        }
        // button is released
        else if (buttonState == HIGH)
        {
            // print out new line
            Serial.println();
 
            // wait before next click is recognized
            delay(100);
        }
    }
}


Als nächstes werde ich die Schaltung und den Programmcode um weitere Knöpfe erweitern.

Thursday, November 13, 2014

Arduino MP3-Player (4) - Die Entwicklungsumgebung

Die Arduino Entwicklungsumgebung (Version 1.0.6) ist für die ersten Schritte ausreichend. Vor allem die enthaltenen Beispiele erleichtern den Einstieg.
Allerdings ist die Entwicklungsumgebung wenig komfortabel sobald man eigenen Code schreiben möchte. Daher habe ich mich nach Alternativen umgesehen, die das Entwickeln erleichtern, weil beispielsweise Codevervollständigung oder die Anzeige von Typen und Methodenparametern vorhanden ist.

Durch das Setzen von definierten Eigenschaften, das Einbinden von gewissen Werkzeugen oder das Installieren von bestimmten Plugins können die weitverbreiteten Entwicklungsumgebungen Microsoft Visual Studio, Atmel Studio oder Eclipse zur Entwicklung von Arduino-Software verwendet werden. Weitere Informationen dazu können unter dem Link http://playground.arduino.cc/Main/DevelopmentTools gefunden werden.

Gerade im Zusammenhang mit Visual Studio oder Atmel Studio, welches auf Visual Studio basiert, ist Arduino IDE für Visual Studio von Visual Micro interessant.

Die Arduino-Entwicklung integriert sich in Visual Studio.
Es können einige Optionen angepasst werden.
Zudem kann über Tools auf einige Funktionen zugegriffen werden.
Über "New" oder "Open" kann ein "Sketch Project" angelegt bzw. geöffnet werden.
 
Anschließend kann das Projekt in der IDE bearbeitet, hochgeladen und überwacht werden.
Mit F5 oder mit einem Klick auf "Local Windows Debugger" wird das Projekt kompiliert und auf das Arduino geladen.
Um den Serial Monitor zu nutzen, muss nach dem hochladen des Projekts immer auf "Show the serial monitor tool window" gedrückt werden.
Es öffnet sich das Fenster, in welchem die serielle Kommunikation angezeigt wird.
Um eine serielle Kommunikation in Arduino herzustellen wird in der setup() Methode diese initialisiert.
Serial.begin(9600);
In der loop() Methode kann dann die serielle Kommunikation verwendet werden. Beispielsweise können Nachrichten gesendet werden.
Serial.print("2 State changed ");
Serial.println(buttonState1);
Dies kann genutzt werden, um Fehler zu finden, was gerade dann sinnvoll ist, wenn kein Debugger zur Verfügung steht.

Als nächstes werde ich mich mit den Knöpfen beschäftigen und versuchen ein Knopfdruck in Arduino zu registrieren.

Friday, November 7, 2014

Arduino MP3-Player (3) - Das erste Einschalten

Es gibt eine Vielzahl von Anleitungen, die die ersten Schritte mit Arduino beschreiben. Ich habe die Anleitung, die es direkt auf der Arduino-Seite gibt verwendet: http://arduino.cc/en/Guide/Windows.

Die Installation der Software und der erste Verbindungsaufbau verlaufen problemlos. BeimVerbinden des Arduino-Boards mit dem PC und dem damit verbundenen Herstellen der Stromversorgung leuchtet die ON-LED grün auf. Weiterhin hat die L-LED angefangen orange zu blinken.
In der Entwicklungsumgebung stehen verschiedene Beispiele zur Verfügung, die schnell ausprobiert werden können. Ohne zusätzliche Hardware kommt 01.Basics->Blink aus. Nach dem Laden kann man sich das Programm in der Entwicklungsumgebung anschauen. Der typische Aufbau eines Arduino-Sketch ist hier zu sehen. Ein Arduino-Sketch besteht aus mindestens zwei Methoden,
void setup() { ... }
und
void loop() { ... }
Die Methode setup() wird einmal beim Einschalten des Arduinos ausgeführt. Die Methode loop() wird anschließend immer wieder in einer Schleife ausgeführt. Sobald die Methode beendet ist, wird sie wieder aufgerufen.

Beim Aufspielen des Beispiel-Sketchs 01.Basics->Blink ergab sich keine Veränderung. Vermutlich war dieses bereits auf dem Arduino-Board. Jedoch wirkte sich das Herumspielen mit den "delay" Werten aus. So konnte die Länge des Aufleuchtens erhöht werden, indem das erste "delay" auf 3000 gesetzt worden ist:  delay(3000);

Das erste Einschalten ist geglückt. Das nächste Mal nehme ich die Entwicklungsumgebung (IDE) genauer unter die Lupe. Bis dahin kann auf den zahlreichen Informationen, die es über Arduino gibt zurückgegriffen werden. Nachfolgend ist hierzu eine Auflistung von Seiten, die mir sehr interessant erschienen.


Informationen auf Arduino.cc:

Arduino - Windows

Arduino Playground - HomePage

Arduino Playground - ManualsAndCurriculum

Arduino - Learn the basics

Arduino - Foundations


Einführungen:

Arduino Comic

Arduino in a Nutshell

Arduino_Tutorial.pdf


Forum:

Arduino Stack Exchange


Informationen auf sparkfun.com:

What is an Arduino? - learn.sparkfun.com

Arduino Comparison Guide - learn.sparkfun.com

Arduino Shields - learn.sparkfun.com

Installing Arduino IDE - learn.sparkfun.com

Data Types in Arduino - learn.sparkfun.com

Analog to Digital Conversion - learn.sparkfun.com

Installing an Arduino Library - learn.sparkfun.com

MP3 Player Shield Hookup - learn.sparkfun.com


Informationen auf Adafruit/Ladyada:

Overview | Adafruit Music Maker Shield | Adafruit Learning System

Arduino Tutorial - Learn electronics and microcontrollers using Arduino!



Thursday, October 30, 2014

Arduino MP3-Player (2) - Das Entwicklungsbrett

Bevor es nun richtig losgeht, nehme ich das Entwicklungsbrett "in Betrieb". Es beinhaltet ein selbstklebendes Steckbrett, das auf das Entwicklungsbrett aufgebracht werden kann, Metallschrauben, Plastik-Abstandsbolzen und Plastik-Muttern zum Fixieren des Arduino-Boards. Weiterhin können Gummifüsse auf die Unterseite des Entwicklungsbretts geklebt werden.
Die Schrauben können durch die Rückseite des Entwicklungsbretts gesteckt werden. Die Abstandsbolzen können dann in diese reingeschraubt werden. Da das Entwicklungsbrett nicht nur für das Arduino UNO Board verwendet werden kann, sind weitere Vorbohrungen vorhanden, sodass hier darauf geachtet werden muss, dass die richtigen Vorbohrungen verwendet werden.
Das Arduino-Board kann nun auf die Abstandsbolzen aufgesteckt und mit den Muttern befestigt werden.
Das Draufschrauben der Muttern klappt an drei Stellen. An der vierten Stelle kann die Mutter nicht angebracht werden, da die Bohrung sich zu dicht an einem Header befindet. Die Fixierung über die übrigen drei Muttern sollte aber ausreichend sein.
Als letztes kann noch das selbstklebende Steckbrett aufgebracht werden.

Jetzt sind wir gewappnet und können als nächstes richtig loslegen und das Arduino-Board in Betrieb nehmen.

Tuesday, October 28, 2014

Arduino MP3-Player (1) - Auswahl der Komponenten


Es wird Zeit sich ein bisschen mit Arduino zu beschäftigen. Und was braucht man als erstes? Klar, ein Projekt!

Man findet sicherlich viele Ideen und Anregungen, wenn man ein bisschen nach Arduino im Netz sucht. Vielleicht findet man ein Beispiel-Projekt, das einem so gut gefällt, dass man es direkt als Vorlage für das eigene Projekt verwenden will. Oder man hat schon eine Idee und sucht sich entsprechende Informationen, Tipps und Komponenten zusammen.

Meine Idee ist es einen MP3-Player zu bauen. Dies ist bestimmt nicht die originellste Idee, aber es ist etwas was ich gebrauchen kann. Und ich möchte nicht einen einfachen MP3-Player bauen, sondern einen mit ein paar Knöpfen, hinter denen Lieder oder Playlisten hinterlegt werden können. In Prinzip schwebt mir sowas vor wie der Hörbert.

Nachdem nun das erster Hindernis genommen ist, kommen wir zum nächsten: Die Komponenten. Ich habe lange herumgesucht, um herauszufinden welche Teile ich benötige (und ganz sicher bin ich mir immer noch nicht). Schließlich aber habe ich mich für einige Komponenten entschieden mit denen ich jetzt erstmal starten werde.

Die erste Komponente ist natürlich ein Arduino-Board. Hierbei habe ich mich für das gängige Arduino UNO R3 entschieden.
Arduino R3 - FrontArduino R3 - Back
Für die ersten Schritte, um mal was auszuprobieren, ist ein Entwicklungsbrett mit einem Steckbrett genau das Richtige.
Das Steckbrett alleine taugt nicht viel, daher: Jede Menge Kabel (M/M und M/F).
Je nach Versuchsaufbau wird auch der ein oder andere Widerstand benötigt.
Damit wären die grundlegenden Komponenten festgelegt. Diese werden eigentlich in jedem Arduino-Projekt benötigt. Etwas spezifischer sind die Knöpfe, die ich benötige, um zu einem Lied oder einer Playlist zu wechseln. Allerdings sind diese hier recht klein, sodass ich später noch andere bestellen werde müssen. Für den Anfang sind diese wohl aber in Ordnung.
Für die kabellose Stromversorgung kann ein Batteriehalter verwendet werden, der direkt in die Buchse der externen Stromversorgung gesteckt werden kann. Es werden 6xAA verwendet. Leider hat dieses Modell keinen Schalter um die Stromversorgung zu unterbrechen.
Zum Ein- und Ausschalten eignet sich ein Kippschalter. Dieser hier ist leider etwas schwergängig.
Für die Regelung der Lautstärke werde ich einen 10k Potentiometer verwenden. Dieser ist keine Schönheit und dreht sich auch nicht sehr leicht. Leider war mein Wunsch-Potentiometer nicht lieferbar.
Damit auch etwas zu hören ist, wird ein Lausprecher benötigt. Dieser 8Ohm 1Watt Lautsprecher hat ein Durchmesser von 7,8cm (3'').
Um MP3s auf dem Arduino wiedergeben zu können, wird ein MP3 Shield mit integriertem SD-Card-Schacht verwendet. Bei diesem Shield handelt es sich um das Adafruit "Music Maker" MP3 Shield for Arduino w/3W Stereo Amp.
Alternativen zu diesm MP3-Shield sind das SparkFun MP3 Player Shield (eveentuell in Kombination mit dem Mono Audio Amp Breakout) oder das ELV Audio Shield für Arduino, ASA1, Komplettbausatz.
Um das Arduino-Board mit dem PC zu verbinden wird noch ein USB-Kabel und zum Aufspielen der MP3s eine Micro-SD-Karte benötigt.

Damit ist unsere Einkaufliste fürs erste fertig. Damit der Bestellvorgang nicht zu aufwendig wird, habe ich alle Komponenten in einem Web-Shop bestellt. Hierbei hat EXP Tech einen relativ guten Eindruck auf mich gemacht. Zum einen ist das Sortiment sehr groß und es werden Produkte aller namhaften Hersteller geführt. Zum anderen waren die Preise im Vergleich zu manch anderem Web-Shop oft günstiger.

Hier nochmal die Einkaufsliste im Überblick:
KomponentePreis
Arduino UNO R323,80€
Prototyping and Development Board5,95€
Jumper Wires Premium 150mm M/F Pack of 101,79€
75 Stück Breadboard Jumper Wires Patchkabel mit M/M Stecker3,50€
Resistor Kit - 1/4W (500 total)7,90€
Tact Button (10 Stück)1,50€
6xAA Battery Holder with DC2.1 Power Jack2,80€
Kippschalter1,58€
Potenziometer 20mm 10k 1,98€
Lautsprecher - 7,8cm (3") Durchmesser - 8Ohm 1Watt1,95€
Adafruit "Music Maker" MP3 Shield for Arduino w/3W Stereo Amp33,27€
USB Kabel 2.0 A-Stecker auf B-Stecker 1.8m1,20€
4GB Micro-SDHC-Karte8,00€
Summe95,22€

Als nächstes werde ich mir das Entwicklungsbrett genauer anschauen.

Tuesday, October 14, 2014

WPF and MVVM - Events

Last time we have seen how to use RelayCommand in ViewModel instead of a click event in View. But what's about all the other events? There is an easy solution where RelayCommand can be used. Therefore we need to add a Reference to the Extension Assembly System.Windows.Interactivity from Blend SDK (for VS2013).



In the XAML part of the View we need to add the namespace that leads to System.Windows.Interactivity from Blend SDK (for VS2013).
<UserControl x:Class="MvvmPassword.LoginView"
             x:Name="This"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
Now we can use i:EventTrigger with the property EventName within Interaction.Triggers to define which event should linked to Command. The Command is defined with the property Command within i:InvokeCommandAction.
        <ComboBox x:Name="UserNames"
                  Grid.Row="1"
                  Grid.Column="1"
                  Margin="4"
                  ItemsSource="{Binding UserNames}"
                  DisplayMemberPath="UserName">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectionChanged">
                    <i:InvokeCommandAction Command="{Binding SelectionCommand}" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </ComboBox>
Next we can just use the RelayCommand in ViewModel, as seen in the last post.
SelectionCommand = new RelayCommand(SelectionChanged);
public ICommand SelectionCommand
{
    get;
    private set;
}
private void SelectionChanged(object parameter)
{
    // ...
}
With the ViewModelBase class, the RelayCommand and the EventTrigger our small MVVM Framework can be taken as a good starting point.

Further Posts

  1. WPF and MVVM - Fundamentals
  2. WPF and MVVM - Commands
  3. WPF and MVVM - Events

Sunday, October 12, 2014

WPF and MVVM - Commands

To extend the MVVM idea, it is necessary to move more code from code-behind of the View to the ViewModel. In the first part we have seen how the properties from the ViewModel can be binded to the View and how changes can be notified.

Now we will see how to move click event handler from code-behind of the View to the ViewModel. Therefore the Command property in XAML of the View is used instead of the click event. The property is set to an instance of an implementation of ICommand. For that we want to have a reusable implementation. One way is to use the DelegateCommand that is also used in the Prism Class Library, the usage is described here. But we want to focus on the simpler implementation of RelayCommand. The RelayCommand builds on WPF Commanding.
public class RelayCommand : ICommand
{
    private readonly Action<object> _Execute;
    private readonly Func<objectbool> _CanExecute;
 
    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
 
    }
 
    public RelayCommand(Action<object> execute, Func<objectbool> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute""Execute cannot be null.");
        }
 
        _Execute = execute;
        _CanExecute = canExecute;
    }
 
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
 
    public void Execute(object parameter)
    {
        _Execute(parameter);
    }
 
    public bool CanExecute(object parameter)
    {
        if (_CanExecute == null)
        {
            return true;
        }
 
        return _CanExecute(parameter);
    }
}
The RelayCommand implements the interface ICommand. Therefore we have to implement the methods Execute and CanExecute. These methods call other methods that you have passed as Action and Func generic delegate to the constructor of the RelayCommand class. Furthermore we have to implement the CanExecuteChanged event. Execute can only be called if CanExecute returns true. If CanExecute depends on one or more properties, then on each change of one of these properties CanExecuteChanged have to be fired so that CanExecute is checked again. To simplify and to automate firing the event, the event is hooked to the static RequerySuggested event of the CommandManager. Doing so the CanExecuteChanged event is called after each action in the UI. In some cases you want to call the CanExecuteChanged event manually (such as CanExecute doesn't depends only on UI properties). Therefore the event hook can be removed and a method is added that can be called manually.
public event EventHandler CanExecuteChanged;
public void OnCanExecuteChanged()
{
    if (CanExecuteChanged != null)
    {
        CanExecuteChanged(thisnew EventArgs());
    }
}
Using the RelayCommand we can move click event handler to the ViewModel. Therefore we add a property whose type is of ICommand.
public ICommand LoginCommand
{
    get;
    private set;
}
The ICommand will be instantiated in the constructor.
LoginCommand = new RelayCommand(Login, CanLogin);
The Execute method of the RelayCommand will call Login.
private void Login(object parameter)
{
    var passwordContainer = parameter as IHavePassword;
    if (passwordContainer != null)
    {
        var secureString = passwordContainer.Password;
        PasswordInVM = ConvertToUnsecureString(secureString);
    }
}
While the CanExecute method of the RelayCommand will call CanLogin.
private bool CanLogin(object parameter)
{
    return !string.IsNullOrEmpty(UserName);
}
At last we need only to add the right Command by binding to the View. We can also pass through a parameter by using CommandParameter. The parameter is set to both methods, Execute and CanExecute.
<Button x:Name="Login"
        Content="Login"
        Command="{Binding LoginCommand}"
        CommandParameter="{Binding ElementName=This}" />
With the RelayCommand class we have our second component of our own MVVM-Framework.

Further Posts

  1. WPF and MVVM - Fundamentals
  2. WPF and MVVM - Commands
  3. WPF and MVVM - Events

Thursday, September 11, 2014

WPF and MVVM - Fundamentals

Model-View-ViewModel (MVVM) is a popular pattern when programming in WPF (Windows Presentation Foundation). It is used to separate the GUI, the business logic and the data. While Model and ViewModel are class files, the View consists of a XAML file and a class file (code-behind) as a User Control.
The patern consists of several components. Sometimes not all components are needed to achieve the goal (and also sometimes MVVM is not needed..). But always the ViewModel needs to be set to the DataContext of the View. This can be done in several ways (here you can found how: Setting the DataContext of a View to a ViewModel in MVVM).
By setting the DataContext of a View, you can bind components of a View to properties of a ViewModel. For example, when you enter in a View some text into a TextBox, the value is set to the binded property in the ViewModel.
<TextBox x:Name="UserName"
         Text="{Binding UserName}" />  
public string UserName
{
    get;
    set;
}
If you are using a Model (maybe a POCO), the Model is very likely a member of your ViewModel. Then your code could look something like that.
public string UserName
{
    get
    {
        return _Model.UserName;
    }
    set
    {
        _Model.UserName = value;
    }
}

You can also bind collections in a ViewModel to a View that act as source for controls like ComboBox, ListBox, DataGrid, and so on.
<ListBox ItemsSource="{Binding Users}"
         DisplayMemberPath="UserName"/> 
public ICollection<User> Users
{
    get
    {
        return _Model.Users;
    }
    set
    {
        _Model.Users = value;
    }
}
public class User
{
    public string UserName
    {
        get;
        set;
    }
 
    // ...
}
To dynamically add elements from a View to a collection in a Model, we need to encapsulate the collection by a ObservableCollection.
Users = new ObservableCollection<User>(_Model.Users);
public ObservableCollection<User> Users
{
    get;
    set;
}
When a new element is added to the ObservableCollection, we also need to add a element to the collection in the Model. Therfor we add an event handler to the CollectionChanged event of the ObservableCollection.
Users.CollectionChanged += Users_CollectionChanged;
void Users_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        foreach (User item in e.NewItems)
        {
            _Model.Users.Add(item);
        }
    }
}
The ViewModel consequently have all properties of the Model. But not only this, it have also properties that affect the View. So maybe you want to disable a Button until a TextBox is not empty.
<Button x:Name="Login"
        Content="Login"
        IsEnabled="{Binding CanLogin}" /> 
public string UserName
{
    get
    {
        return _Model.UserName;
    }
    set
    {
        _Model.UserName = value;
        CanLogin = !string.IsNullOrEmpty(value);
    }
} 
public bool CanLogin
{
    get;
    set;
}
But this will not work until now. The View doesn't realize that the property CanLogin has changed. To let the View realize that, we need to implement the INotifyPropertyChanged interface. If we move the implementation of the interface in an own class, we can use this base class for all ViewModels. Here is an example of the implementation of the INotifyPropertyChanged:
public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
 
    public void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(thisnew PropertyChangedEventArgs(name));
        }
    }
}
public class LoginViewModel : ViewModelBase
Now we can call the OnPropertyChanged method to let the View know that some property has changed. The method name of the changed property is used as parameter.
public bool CanLogin
{
    get
    {
        return _canLogin;
    }
    set
    {
        _canLogin = value;
        OnPropertyChanged("CanLogin");
    }
}
With the ViewModelBase class we have our first component of our own MVVM-Framework.

Further Posts

  1. WPF and MVVM - Fundamentals
  2. WPF and MVVM - Commands
  3. WPF and MVVM - Events

Friday, August 29, 2014

Setting the DataContext of a View to a ViewModel in MVVM

Setting the DataContext of a View to a ViewModel can be done in various ways. It can be done in the constructor of the View
public LoginView()
{
 InitializeComponent();
 DataContext = new LoginViewModel();
}
or
public LoginView(LoginViewModel viewModel)
{
 InitializeComponent();
 DataContext = viewModel;
}
or directly in the XAML
<UserControl x:Class="MvvmPassword.LoginView"
      x:Name="This"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:MvvmPassword">
    <UserControl.Resources>
        <local:LoginViewModel x:Key="ViewModel" />
    </UserControl.Resources>
    <Grid DataContext="{Binding Source={StaticResource ViewModel}}"> 
        ... 
    </Grid>
</UserControl>
or
<UserControl x:Class="MvvmPassword.LoginView"
      x:Name="This"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="clr-namespace:MvvmPassword"> 
 <UserControl.DataContext>
  <local:LoginViewModel x:Name="ViewModel" />
 </UserControl.DataContext>
 <Grid> 
           ...
        </Grid>
</UserControl>

Thursday, August 21, 2014

WPF TreeView with Multiple Selection

With the WPF TreeView it is not possible to select multiple items. But it can be easily extended. Therefore an Attached Property can be used. First we are adding the used namespace to the control.
xmlns:e="clr-namespace:MultipleSelectionTreeView;
                                            assembly=MultipleSelectionTreeView"
Then we can add the Attached Property to the TreeView. With IsMultipleSelection="True" we activate the multiple selection in the TreeView. With the property SelectedItems we can bind the multiple selected items to a property of the DataContext. We can define the style of a multiple selected TreeViewItem within a Style Trigger. Therefore the property IsItemSelected is used.
<TreeView ItemsSource="{Binding Elements}"
          MinHeight="20"
          e:TreeViewMultipleSelectionAttached.IsMultipleSelection="True"
          e:TreeViewMultipleSelectionAttached.SelectedItems=
                                                   "{Binding SelectedElements}">
    <TreeView.Resources>
        <Style x:Key="{x:Type TreeViewItem}" BasedOn="{StaticResource 
                                                        {x:Type TreeViewItem}}" 
                                                        TargetType=
                                                        "{x:Type TreeViewItem}">
            <Style.Triggers>
                <Trigger 
                   Property="e:TreeViewMultipleSelectionAttached.IsItemSelected"
                         Value="True">
                    <Setter Property="Background"
                            Value="LightGreen" />
                </Trigger>
            </Style.Triggers>
        </Style>
        <HierarchicalDataTemplate DataType="{x:Type e:Element}"
                                  ItemsSource="{Binding ChildElements}">
            <StackPanel Orientation="Horizontal"
                        VerticalAlignment="Stretch"
                        Margin="0,2,0,2">
                <TextBlock Text="{Binding ElementName}"
                           Margin="5,0,0,0"
                           VerticalAlignment="Center" />
            </StackPanel>
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate DataType="{x:Type e:ChildElement}">
            <StackPanel Orientation="Horizontal"
                        VerticalAlignment="Stretch"
                        Margin="0,2,0,2">
                <TextBlock Text="{Binding ElementName}"
                           VerticalAlignment="Center"
                           Margin="5,0,0,0" />
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>
Now have a look at the implementation of the Attached Property. To create a custom Attached Property a static DependencyProperty is defined by using the RegisterAttached method. This method has the possibility to set a PropertyMetadata as parameter in that a PropertyChangedCallback can be defined by using Delegates. In the callback method an event handler is added or removed to DependencyObject. First we define the IsMultipleSelection property. With that a TreeView is marked in XAML to have multiple selection abilities.
    public static readonly DependencyProperty IsMultipleSelectionProperty =
        DependencyProperty.RegisterAttached(
            "IsMultipleSelection",
            typeof(Boolean),
            typeof(TreeViewMultipleSelectionAttached),
            new PropertyMetadata(false, OnMultipleSelectionPropertyChanged));
 
    public static bool GetIsMultipleSelection(TreeView element)
    {
        return (bool)element.GetValue(IsMultipleSelectionProperty);
    }
 
    public static void SetIsMultipleSelection(TreeView element, Boolean value)
    {
        element.SetValue(IsMultipleSelectionProperty, value);
    }
In the RegisterAttached method we have defined a PropertyMetadata object that defines a call back method (OnMultipleSelectionPropertyChanged) that is called if the property has changed. In that method a handler for the MouseLeftButtonDownEvent of TreeViewItem is added or removed to the TreeView. Remember to mark the handledEventsToo parameter in the AddHandler method as true to allow calling the handler OnTreeViewItemClicked correctly.
    private static void OnMultipleSelectionPropertyChanged(DependencyObject d,
                                         DependencyPropertyChangedEventArgs e)
    {
        TreeView treeView = d as TreeView;
 
        if (treeView != null)
        {
            if (e.NewValue is bool)
            {
                if ((bool)e.NewValue)
                {
                    treeView.AddHandler(TreeViewItem.MouseLeftButtonDownEvent,
                      new MouseButtonEventHandler(OnTreeViewItemClicked), true);
                }
                else
                {
                   treeView.RemoveHandler(TreeViewItem.MouseLeftButtonDownEvent,
                        new MouseButtonEventHandler(OnTreeViewItemClicked));
                }
            }
        }
    }
The OnTreeViewItemClicked method is now called on each click at a TreeViewItem if IsMultipleSelection for a TreeView is set to true.
    private static void OnTreeViewItemClicked(object sender, MouseButtonEventArgs e)
    {
        TreeViewItem treeViewItem = FindTreeViewItem(
                                        e.OriginalSource as DependencyObject);
        TreeView treeView = sender as TreeView;
 
        if (treeViewItem != null && treeView != null)
        {
            if (Keyboard.Modifiers == ModifierKeys.Control)
            {
                SelectMultipleItemsRandomly(treeView, treeViewItem);
            }
            else if (Keyboard.Modifiers == ModifierKeys.Shift)
            {
                SelectMultipleItemsContinuously(treeView, treeViewItem);
            }
            else
            {
                SelectSingleItem(treeView, treeViewItem);
            }
        }
    }
First we need to find the TreeViewItem that is invoked with the click. This happens with the recursive FindTreeViewItem method.
    private static TreeViewItem FindTreeViewItem(DependencyObject dependencyObject)
    {
        if (dependencyObject == null)
        {
            return null;
        }
 
        TreeViewItem treeViewItem = dependencyObject as TreeViewItem;
        if (treeViewItem != null)
        {
            return treeViewItem;
        }
 
        return FindTreeViewItem(VisualTreeHelper.GetParent(dependencyObject));
    }
Then the OnTreeViewItemClicked method checks what kind of click is happening. There are Left-Mouse-Button (LMB) + Ctrl, LMB + Shift, and all other cases with LMB. In the third case a single click is performed.
    private static void SelectSingleItem(TreeView treeView,
                                                    TreeViewItem treeViewItem)
    {
        // first deselect all items
        DeSelectAllItems(treeView, null);
        SetIsItemSelected(treeViewItem, true);
        SetStartItem(treeView, treeViewItem);
    }
Therefore at first all items are deselected by calling the recursive method DeSelectAllItems. 
    private static void DeSelectAllItems(TreeView treeView,
                                                 TreeViewItem treeViewItem)
    {
        if (treeView != null)
        {
            for (int i = 0; i < treeView.Items.Count; i++)
            {
                TreeViewItem item = treeView.ItemContainerGenerator.
                                           ContainerFromIndex(i) as TreeViewItem;
                if (item != null)
                {
                    SetIsItemSelected(item, false);
                    DeSelectAllItems(null, item);
                }
            } 
        }
        else
        {
            for (int i = 0; i < treeViewItem.Items.Count; i++)
            {
                TreeViewItem item = treeViewItem.ItemContainerGenerator.
                                           ContainerFromIndex(i) as TreeViewItem;
                if (item != null)
                {
                    SetIsItemSelected(item, false);
                    DeSelectAllItems(null, item);
                }
            } 
        }
    }
Selection and deselection is happening by the DependencyProperty IsItemSelected that we have also defined. This property is used in XAML to apply a certain style on the selected TreeViewItems.
    public static readonly DependencyProperty IsItemSelectedProperty =
        DependencyProperty.RegisterAttached(
            "IsItemSelected",
            typeof(Boolean),
            typeof(TreeViewMultipleSelectionAttached),
            new PropertyMetadata(false, OnIsItemSelectedPropertyChanged));
 
    public static bool GetIsItemSelected(TreeViewItem element)
    {
        return (bool)element.GetValue(IsItemSelectedProperty);
    }
 
    public static void SetIsItemSelected(TreeViewItem element, Boolean value)
    {
        element.SetValue(IsItemSelectedProperty, value);
    }
In the RegisterAttached method we have defined a PropertyMetadata object that defines a call back method (OnIsItemSelectedPropertyChanged) that is called if the property (selection of the TreeViewItem) has changed. In that method the Header of TreeViewItem will be added to or removed to a List of all selected TreeViewItems. This List is associated with the corresponding TreeView.
    private static void OnIsItemSelectedPropertyChanged(DependencyObject d,
                                           DependencyPropertyChangedEventArgs e)
    {
        TreeViewItem treeViewItem = d as TreeViewItem;
        TreeView treeView = FindTreeView(treeViewItem);
        if (treeViewItem != null && treeView != null)
        {
            var selectedItems = GetSelectedItems(treeView);
            if (selectedItems != null)
            {
                if (GetIsItemSelected(treeViewItem))
                {
                    selectedItems.Add(treeViewItem.Header);
                }
                else
                {
                    selectedItems.Remove(treeViewItem.Header);
                }
            }
        }
    }
To find the TreeView corresponding to the TreeViewItem the recursive method FindTreeView is used.
    private static TreeView FindTreeView(DependencyObject dependencyObject)
    {
        if (dependencyObject == null)
        {
            return null;
        }
 
        TreeView treeView = dependencyObject as TreeView;
        if (treeView != null)
        {
            return treeView;
        }
 
        return FindTreeView(VisualTreeHelper.GetParent(dependencyObject));
    }
To associate the List of all selected TreeViewItems with the corresponding TreeView the DependencyProperty SelectedItems is used. This property can be used in XAML to bind the multiple selected items to a property of the DataContext.
    public static readonly DependencyProperty SelectedItemsProperty =
        DependencyProperty.RegisterAttached(
            "SelectedItems",
            typeof(IList),
            typeof(TreeViewMultipleSelectionAttached),
            new PropertyMetadata());
 
    public static IList GetSelectedItems(TreeView element)
    {
        return (IList)element.GetValue(SelectedItemsProperty);
    }
 
    public static void SetSelectedItems(TreeView element, IList value)
    {
        element.SetValue(SelectedItemsProperty, value);
    }
Now let's go back to the SelectSingleItem method. After deselecting all TreeViewItems by setting the IsItemSelected DependencyProperty to false, the clicked TreeViewItem is marked as selected by setting the IsItemSelected DependencyProperty to true. After that the TreeViewItem is marked as StartItem within the corresponding TreeView. Therefore the private StartItem DependencyProperty is used. The StartItem DependencyProperty is used as starting point for a subsequent multiple selection where a continuous range is selected.
    private static readonly DependencyProperty StartItemProperty =
        DependencyProperty.RegisterAttached(
            "StartItem",
            typeof(TreeViewItem),
            typeof(TreeViewMultipleSelectionAttached),
            new PropertyMetadata());
 
    private static TreeViewItem GetStartItem(TreeView element)
    {
        return (TreeViewItem)element.GetValue(StartItemProperty);
    }
 
    private static void SetStartItem(TreeView element, TreeViewItem value)
    {
        element.SetValue(StartItemProperty, value);
    }
If a LMB + Ctrl is performed the method SelectMultipleItemsRandomly is called. In this method the IsItemSelected DependencyProperty of the clicked TreeViewItem is toggled. Furthermore the StartItem DependencyProperty will be set of not already set or unset if no TreeViewItem is selected anymore.
    private static void SelectMultipleItemsRandomly(TreeView treeView,
                                                    TreeViewItem treeViewItem)
    {
        SetIsItemSelected(treeViewItem, !GetIsItemSelected(treeViewItem));
        if (GetStartItem(treeView) == null)
        {
            if (GetIsItemSelected(treeViewItem))
            {
                SetStartItem(treeView, treeViewItem);
            }
        }
        else
        {
            if (GetSelectedItems(treeView).Count == 0)
            {
                SetStartItem(treeView, null);
            }
        }
    }
If a LMB + Shift is performed the method SelectMutlipleItemsContinuously is called. If no StartItem is set, no action takes place. If the selected TreeViewItem is equal to the startItem then the SelectSingleItem method is called and then the method is returned. If both conditions not met multiple continuously TreeViewItems selection takes place.
    private static void SelectMultipleItemsContinuously(TreeView treeView,
                                                     TreeViewItem treeViewItem)
    {
        TreeViewItem startItem = GetStartItem(treeView);
        if (startItem != null)
        {
            if (startItem == treeViewItem)
            {
                SelectSingleItem(treeView, treeViewItem);
                return;
            }
 
            ICollection<TreeViewItem> allItems = new List<TreeViewItem>();
            GetAllItems(treeView, null, allItems);
            DeSelectAllItems(treeView, null);
            bool isBetween = false;
            foreach (var item in allItems)
            {
                if (item == treeViewItem || item == startItem)
                {
                    // toggle to true if first element is found and
                    // back to false if last element is found
                    isBetween = !isBetween;
 
                    // set boundary element
                    SetIsItemSelected(item, true);
                    continue;
                }
 
                if (isBetween)
                {
                    SetIsItemSelected(item, true);
                }
            }
        }
    }
With the GetAllItems method all TreeViewItems of a TreeView are recursively collected into a collection. Furthermore all items are deselected by the DeSelectAllItems method.
    private static void GetAllItems(TreeView treeView, TreeViewItem treeViewItem,
                                    ICollection<TreeViewItem> allItems)
    {
        if (treeView != null)
        {
            for (int i = 0; i < treeView.Items.Count; i++)
            {
                TreeViewItem item = treeView.ItemContainerGenerator.
                                           ContainerFromIndex(i) as TreeViewItem;
                if (item != null)
                {
                    allItems.Add(item);
                    GetAllItems(null, item, allItems);
                }
            }
        }
        else
        {
            for (int i = 0; i < treeViewItem.Items.Count; i++)
            {
                TreeViewItem item = treeViewItem.ItemContainerGenerator.
                                           ContainerFromIndex(i) as TreeViewItem;
                if (item != null)
                {
                    allItems.Add(item);
                    GetAllItems(null, item, allItems);
                }
            }
        }
    }
Then each element of the list will be checked, if it is in the range of StartItem and selected TreeViewItem. If this is true the TreeViewItem will be marked as selected.