Posts Tagged MVVM

GPIO Test Application Part 4 – ViewModel

Previous: GPIO Test Application Part 3 – Model

This post is part 4 of 6 of the GPIO Test Application. You can find the initial post here: GPIO Test Application part 1 – Overview.

The ViewModel (VM) encapsulate all the functionality provided by the application.Each page, user control or dialog should have it’s own VM to implement the page, user control or dialog functionality.

For our Main Page, we have a MainPageVM which is the view model for this page.

MainPageVM vs Model

The MainPageVM creates and maintains the current instance of the LogicGateModel and makes it available to the UI. MainPageVM also performs operations on the Model.

Here, we see the intialization code that creates the Model:

private void Initialize()
{
    Logger.AddLogEntry("Initializing Main Window VM!");

    InitializeCommands();

    m_Model = new LogicGateModel();
    m_Model.PropertyChanged += Model_PropertyChanged;
    RecalculateOutput();

    Logger.AddLogEntry("Done Initializing Main Window VM!");
}

...

private LogicGateModel m_Model = null;
/// <summary>
/// This is the instance of the Model.
/// </summary>
public LogicGateModel Model
{
    get
    {
        return m_Model;
    }
}

When the model is created, the MainPageVM attaches an event handler to the PropertyChanged event of the LogicGateModel.

The handler is used to detect and act uppon changes in the values of LeftSwitch and RightSwitch. When those properties are changed, the MainPageVM recalculates the required outputs and updates the Model accordingly.  It also recalculates on changes to the IsEnabled function.

Bellow is the Model_PropertyChanged event handler code:

private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "LeftSwitch" || e.PropertyName == "RightSwitch")
    {
        RecalculateOutput();
    }
    else if(e.PropertyName== "IsEnabled")
    {
        if(Model.IsEnabled)
        {
            OnPropertyChanged("ShowSimulatedSwitches");
            RecalculateOutput();
        }
    }
}

MainPageVM functionality

Calculations

The calculation portion of the MainPageVM is pretty simple. A case statement performs the actual logic operation on the RightSwitch and LeftSwitch of the Model, then the result is pushed back to the RedLED (false) and GreenLED (true) properties of the model.

private void RecalculateOutput()
{
    Logger.AddLogEntry("Recalculating output!");

    bool result = false;
    switch(Logic)
    {
        case LogicTypeEnum.AND:
            result = Model.LeftSwitch && Model.RightSwitch;
            break;
        case LogicTypeEnum.OR:
            result = Model.LeftSwitch || Model.RightSwitch;
            break;
        case LogicTypeEnum.NAND:
            result = !(Model.LeftSwitch && Model.RightSwitch);
            break;
        case LogicTypeEnum.NOR:
            result = !(Model.LeftSwitch || Model.RightSwitch);
            break;
        case LogicTypeEnum.XOR:
            result = Model.LeftSwitch ^ Model.RightSwitch;
            break;
    }

    Model.GreenLED = result;
    Model.RedLED = !result;

}

Note: We could have used a more powerful pattern, such as the Strategy pattern, to select and perform the logic operation. But in the spirit of keeping the code readable a case statement is better.

Logic Property

The formula used to calculate the output is dependent on user input. The user selects the logic operation to perform then the application uses it to calculate the output. The Logic property of the MainPageVM is used to define the operation currently used. The MainPageVM also provides a list of available operations so that the View is able to display a list to the user. Following is the code related to the Logic property.

LogicTypeEnum represents the available operations.

public enum LogicTypeEnum
{
    AND,
    OR,
    NAND,
    NOR,
    XOR
}

Logic and LogicList properties provide the functionality to the View. Notice the call to RecalculateOutput on the Logic setter.

private LogicTypeEnum m_Logic = LogicTypeEnum.AND;
/// <summary>
/// This is the logic applied to the inputs to define how calculation is done.
/// </summary>
public LogicTypeEnum Logic
{
    get
    {
        return m_Logic;
    }
    set
    {
        if(value != m_Logic)
        {
            m_Logic = value;
            OnPropertyChanged();
            RecalculateOutput();
        }
    }
}

private List<LogicTypeEnum> m_LogicList = null;
/// <summary>
/// This is the list to populate the list or ddl used to select
/// the logic to apply on the inputs. 
/// </summary>
public List<LogicTypeEnum> LogicList
{
    get
    {
        if(m_LogicList == null)
        {
            m_LogicList = new List<LogicTypeEnum>();
            Array a = Enum.GetValues(typeof(LogicTypeEnum));
            foreach(var v in a)
            {
                m_LogicList.Add((LogicTypeEnum)v);
            }
        }
        return m_LogicList;
    }
}

Other Functionality

Other functionality have also been added to the MainFormVM. For debugging purpose the UI can display buttons on the Main Page simulating the right and left switches. Those buttons are only available if the GPIO fails to initialize which in turn enables the ShowSimulatedSwitches property on the VM.

There is also an Exit command that will close the application. This is useful to stop debugging sessions from the Main Page.

Conclusion

The MainPageVM performs all the function related to the calculation and maintains the information related to the calculation. This allows the Model to focus on content and not worry about functionality.

Next: GPIO Test Application Part 5 – View

, , , , ,

Leave a comment

GPIO Test Application Part 3 – Model

Previous: GPIO Test Application Part 2 – Hardware

This post is part 3 of 6 of the GPIO Test Application. You can find the initial post here: GPIO Test Application part 1 – Overview.

The model of our application will provide the data used by the application. The data in this case represents the information that is sent and received on the GPOI.

Model Application Interface

The Model exposes a simple and easy to use interface to the application. This interface hides all the complexity of working with the GPIO. The properties for the interface look like regular content properties of an INotifyPropertyChange object.

The following properties compose this interface:

  • RightSwitch: bool value that returns true when the right switch is pressed.
  • LeftSwitch: bool value that returns true when the left switch is pressed.
  • RedLED: bool value that represents if the red LED is on / off. Setting this value will turn the green LED on.
  • GreenLED: bool value that represents if the green LED is on / off. Setting this value will turn the green LED on.
  • IsEnabled: read only bool value that specify if the GPIOs have been successfully initialized and are available. This property is used to enter simulation mode. In simulation mode, the application adds buttons on the screen to simulate the left and right push buttons on the screen. This is useful for debugging the application on your computer.

The switch properties looks like this:

private bool m_RightSwitch = true;

/// <summary>
/// Provides access to the value of the Right push switch on the hardware
/// </summary>
public bool RightSwitch
{
    get
    {
        return m_RightSwitch;
    }
    set
    {
        if (value != m_RightSwitch)
        {
            m_RightSwitch = value;
            OnPropertyChanged();
        }
    }
}

The LED properties looks like this:

private bool m_RedLED = false;

/// <summary>
/// Reads and Sets the value of the red LED
/// </summary>
public bool RedLED
{
    get
    {
        return m_RedLED;
    }
    set
    {
        if (value != m_RedLED)
        {
            m_RedLED = value;
            UpdateGPIO(m_RedLEDPin, value);
            OnPropertyChanged();
        }
    }
}

Notice that in the setter of the LED properties, a call is made to UpdateGPIO to send the value to the GPIO.

GPIO processing

In the case of our application, the Model is encapsulating all the GPIO access. The flow of GPIO runs something like this:

  1. Initialize the GPIO
  2. While the application is running:
    1. Wait for events on the Input pins
    2. Write Outputs to the Output pins
  3. Shut down the GPIO

We keep the instance of the GpioPin for each pin that we use. The GpioPin are created in the initialization process and released in the dispose. For simplicity, we keep the pin numbers in constants fields.

private const int GPIO_LEFT_SWITCH = 21;
private const int GPIO_RIGHT_SWITCH = 20;
private const int GPIO_RED_LED = 6;
private const int GPIO_GREEN_LED = 5;

private GpioController m_Controller = null;
private GpioPin m_LeftSwitchPin = null;
private GpioPin m_RightSwitchPin = null;
private GpioPin m_RedLEDPin = null;
private GpioPin m_GreenLEDPin = null;

Initializing / Shutting down the GPIO

During the initialization process the following sequence occurs:

  1. The application attempts to retrieve the default GpioController. If the controller cannot be retrieved, this means that the device does not support GPIO.
  2. All the pins are initialized.
  3. If all the pins are initialized correctly, the IsEnabled property is set to true.
  4. If one of the pins fails to initialize, the Release() function is called to free up all the allocated resources.
private void Initialize()
{
    Logger.AddLogEntry("Initializing GPIO on LogicGateModel");

    m_Controller = GpioController.GetDefault();
    if (m_Controller != null)
    {
        string startupMessage = "Startup:\r\n";
        bool success = true;

        success &= InitializeLEDPin(GPIO_GREEN_LED, "green LED", out m_GreenLEDPin, ref startupMessage);
        success &= InitializeLEDPin(GPIO_RED_LED, "red LED", out m_RedLEDPin, ref startupMessage);

        success &= InitializeSwitchPin(GPIO_LEFT_SWITCH, "left switch", out m_LeftSwitchPin, ref startupMessage);
        success &= InitializeSwitchPin(GPIO_RIGHT_SWITCH, "right switch", out m_RightSwitchPin, ref startupMessage);

        Message = startupMessage;
        if (!success)
        {
            Release();
        }
        else
        {
            // Read the current values...
            LeftSwitch = m_LeftSwitchPin.Read() == GpioPinValue.High;
            RightSwitch = m_RightSwitchPin.Read() == GpioPinValue.High;

            IsEnabled = true;
        }
    }
    else
    {
        Message = "Unable to access the GpioController!";
    }

    Logger.AddLogEntry("Initialization of GPIO on LogicGateModel complete!");
}

When the LED pins are created:

  1. We use the controller to try to open up the pin
  2. If that succeeds, we make sure the pins supports the Output Drive Mode.
  3. Then we set the Drive Mode to Output
  4. If any of the operations do not succeed, we set the result to false to indicate that there was an error initializing the GPIO
private bool InitializeLEDPin(int pin, string pinName, out GpioPin output, ref string message)
{
    bool result = true;

    Logger.AddLogEntry("Initializing Pin #{0}, {1}", pin, pinName);

    GpioOpenStatus status;
    if (m_Controller.TryOpenPin(pin, GpioSharingMode.Exclusive, out output, out status))
    {
        if (output.IsDriveModeSupported(GpioPinDriveMode.Output))
        {
            output.SetDriveMode(GpioPinDriveMode.Output);
            message += string.Format("   Pin {0} initialized: status = {1}\r\n", pinName, status.ToString());
        }
        else
        {
            message += string.Format("   Pin {0} error: Drive mode {1} not supported.\r\n", pinName, status.ToString());
            result = false;
        }
    }
    else
    {
        message += string.Format("   Can't initialize {0}: status = {1}\r\n", pinName, status.ToString());
        result = false;
    }

    return result;
}

When the switch pins are created:

  1. We use the controller to try to open up the pin
  2. If that succeeds, we make sure the pins supports the Output Drive Mode.
  3. Then we set the Drive Mode to Output
  4. Then we attach an event handler to the ValueChanged event of the GpioPinto  to receive notification whenever there is a change of state on the pin.
  5. If any of the operations do not succeed, we set the result to false to indicate that there was an error initializing the GPIO
private bool InitializeSwitchPin(int pin, string pinName, out GpioPin output, ref string message)
{
    bool result = true;

    Logger.AddLogEntry("Initializing Pin #{0}, {1}", pin, pinName);

    GpioOpenStatus status;
    if (m_Controller.TryOpenPin(pin, GpioSharingMode.Exclusive, out output, out status))
    {
        if (output.IsDriveModeSupported(GpioPinDriveMode.Input))
        {
            output.SetDriveMode(GpioPinDriveMode.Input);

            //Intentional, we do not use debounce this time but this is where you would set it
            //output.DebounceTimeout = TimeSpan.FromMilliseconds(5);

            output.ValueChanged += PinValueChanged;
            message += string.Format("   Pin {0} initialized: status = {1}\r\n", pinName, status.ToString());
        }
        else
        {
            message += string.Format("   Pin {0} error: Drive mode {1} not supported.\r\n", pinName, status.ToString());
            result = false;
        }
    }
    else
    {
        message += string.Format("   Can't initialize {0}: status = {1}\r\n", pinName, status.ToString());
        result = false;
    }

    return result;
}

The Release function is strait forward, it disconnects the event handler from the input pins and calls Dispose on each of the pins.

private void Release()
{
    Logger.AddLogEntry("Releasing GPIO on LogicGateModel");

    if (m_Controller != null)
    {
        // Release all the GPIO pins used
        if (m_LeftSwitchPin != null)
        {
            m_LeftSwitchPin.ValueChanged -= PinValueChanged;
            m_LeftSwitchPin.Dispose();
        }

        if (m_RightSwitchPin != null)
        {
            m_RightSwitchPin.ValueChanged -= PinValueChanged;
            m_RightSwitchPin.Dispose();
        }

        if (m_GreenLEDPin != null)
        {
            m_GreenLEDPin.Dispose();
        }

        if (m_RedLEDPin != null)
        {
            m_RedLEDPin.Dispose();
        }
    }
    Logger.AddLogEntry("Completed Releasing GPIO on LogicGateModel");
}

Handling OnChange event on the input PINs

When initializing the pins we assigned an event handler to the ValueChanged event of the input pins. The following code shows the OnValueChanged function:

private void PinValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
    // normally we would use the arg to get the value, as follow:
    //    (args.Edge == GpioPinEdge.FallingEdge);
    // but at this point it is completely unreliable and returns garbage
    bool value = sender.Read() == GpioPinValue.High;

    if (sender == m_LeftSwitchPin)
    {
        CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
        {
            LeftSwitch = value;
        });
    }
    else if (sender == m_RightSwitchPin)
    {
        CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
        {
            RightSwitch = value;
        });
    }
}

We first read the value of the pin by calling the Read() function. The GpioPin that raised the event is received as the sender parameter, so we use it to identify which button was pressed. Then we just set the value on the appropriate property.

One particularity of this code is that the ValueChange event is raised on a separate thread. In order to interact with the User Interface, we need to bring the call back on the UI thread. That is done here with the CoreApplication.MainView.Dispatcher.RunAsync call.

Turning LEDs On/Off

When the ViewModel sets the value of the RedLED or GreenLED, we need to send that value GPIO, the UpdateGPOI function does that for us:

private void UpdateGPIO(GpioPin pin, bool value)
{
    if (IsEnabled && pin != null)
    {
        GpioPinValue v = value ? GpioPinValue.Low : GpioPinValue.High;
        pin.Write(v);
    }
}

Conclusion

The Model already provides a big portion of the overall functionality we require. It reads inputs from the GPIO, maintains the current state of Input/Outputs and provides a way to turn LEDs on and off. All that using a simple interface that the rest of the application can use.

Next: GPIO Test Application Part 4 – ViewModel

, , , ,

Leave a comment

GPIO Test Application part 1 – Overview

This post is part 1 of 6 of the GPIO Test Application. The following parts together compose the full article:

As a first application that involves hardware, I decided to keep things simple. The idea is just to hook up a couple Inputs and Outputs and have a program that will work with the GPIO to read/write the values.

Source Code

To follow along, you might want to download the full solution right here:

You can run the application on your computer, but it will run in simulation mode (no GPOI). See the First Windows 10 IoT Core Windows Application post for information on how to run it on your Raspberry Pi.

The source code for the project has the same requirements as stated in my post: Setting up Visual Studio 2015 for Windows 10 IoT Core development

Requirements

The requirements that I chose for this application will allow us to explore the GPIO and programming model associated with creating User Interface on the Raspberry Pi 2 under the Windows 10 IoT Core operating system. The application should incorporate:

  • Hardware components accessed through the GPIO
  • A responsive UI that will display the status of the hardware components as it changes.
  • And some control by the user to set how the output are calculated.

For simplicity, this application assumes the Raspberry Pi is configured for user interactivity and that it is connected to a full size screen and has an input pointing device. I will explore different form factors as well as headless device development at another time.

Solution

To achieve these requirements I will create a programmable LogicGate application that will take input from 2 push buttons, will calculate a result based on a logic operation applied to the 2 inputs, and will light up a green LED for true and a red LED for false.

In addition, we will include UI to control the calculation by selecting which logic operation is used. We will also include UI to visually display the status of the Inputs and Outputs.

Whenever any of the inputs or logic operation parameter changes, the application should update the displayed status, recalculate the output and light up the appropriate LED.

Application Components Overview

The application will be based on the MVVM pattern that we have shown earlier. This pattern is well adapted to the creation of reactive WPF applications. It this case, the application is composed of 4 main components that are:

  • Hardware
  • Model
  • View Model
  • View

The components are organized as follow:

LogicGateOverview

Hardware

The hardware is composed of:

  • 2 switches (right / left)
  • 1 red LED
  • 1 green LED

More details about the electronic circuit will be available in the following post GPIO Test Application Part 2 – Hardware.

Model

On this application , we will used the model to encapsulate the access to the hardware through the GPIOs.

The model will provide a comprehensive Model API that allows seamless access to read the Inputs and write to the Outputs. The interface will contain the following Properties:

  • SwitchRight
  • SwitchLeft
  • RedLED
  • GreenLED

More details about the Model will be available in the post GPIO Test Application Part 3 – Model.

ViewModel

The ViewModel contains an instance of the Model and makes it available to the View. It also provides the functionality to calculate the Output based on the Logic Operation selected by the user on the View.

The available Logic Operations are:

  • AND
  • OR
  • NAND
  • NOR
  • XOR

More details about the ViewModel will be available in the post GPIO Test Application Part 4 – ViewModel.

View

The View provides the User Interface that the user to view the hardware status and to select the Logic Operation that determines the output. The user interface will look something like this:ViewScreenShot

Where:

  • Logic Operation: Section that allows the user to select which logic operation is used to calculate the output.
  • Device Interface: Displays the symboles that represents the different hardware components. The symbol is only displayed when the switch is pressed or the LED is lit. The symbols are:
    • Blue Square: for the switches
    • Red Circle: for the red LED
    • Green Circle: for the green LED

More details about the View will be available in the post GPIO Test Application Part 5 – View.

Results

In the last section, GPIO Test Application Part 6 – Result,  I will provide access to the full source code of this application and we’ll see the resulting application in action.

Next: GPIO Test Application Part 2 – Hardware

, , , , , ,

Leave a comment

Improvement on First Windows 10 IoT Core Windows Application

Now that I have a working first application working, I wanted to see how far I can take the application writing model on the Raspberry Pi.

Our first Windows application is a WPF application.  Let see if we can leverage the full power of WPF and take it to the next level.

Why

WPF application will work with hardcoded event handlers on the page. But there is a better more comprehensive way to do WPF. It’s called MVVM.

Being a purist, I would much rather do things correctly. So I want to make sure WPF for Windows 10 IoT Core applications support MVVM.

MVVM is a bit overkill for this small application. But once our applications start becoming more and more complicated, MVVM will make things a lot easier to manage.

WPF, XAML and MVVM

Before we start, note that I will not go into details here. MVVM is a much wider and complicated field than what this little blog post can answer. I’m just going to give you a quick overview and provide you with an example. If you need more information feel free to google it.

MVVM (Model, View, ViewModel) is a design pattern. One of the most powerful feature of MVVM is that it can separate the UI from the functionality. MVVM is composed of 3 different components:

  • Model: Contains the data.
  • View: Provides a screen.
  • ViewModel: Provides the functionality.

WPF is particularly well suited for the MVVM pattern.

XAML is the View language for WPF application. XAML is a declarative language that can be edited completely separate from the actual functionality code. This means that the developers can write all the functionality, and then let the designers build the screen.

What we need for MVVM under WPF is:

  • Data Binding
  • Commanding support

My take on MVVM

You will notice that we do not implement a Model. That is because there is no real data our application manages.

Let’s get started:

Utility Classes

First I need a couple of utility classes to support Data Binding and Commands.

BaseVM: Implements the INotifyPropertyChanged interface so that the functionality (ViewModel) can notify the UI that something changed. Here is the code:

public class BaseVM : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        Debug.WriteLine("OnPropertyChanged: {0}", propertyName);
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

VMCommand: Implements the ICommand interface so that the UI can send commands to the functionality (ViewModel). Here is the code:

public delegate bool CanExecuteDelegate(object o);
public delegate void ExecuteDelegate(object o);

public class VMCommand : ICommand
{
    public VMCommand(ExecuteDelegate execute)
    {
        ExecuteFunction = execute;
        CanExecuteFunction = null;
    }

    public VMCommand(ExecuteDelegate execute, CanExecuteDelegate canexec)
    {
        ExecuteFunction = execute;
        CanExecuteFunction = canexec;
    }

    private ExecuteDelegate ExecuteFunction { get; set; }
    private CanExecuteDelegate CanExecuteFunction { get; set; }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        bool result = true;
        if (CanExecuteFunction != null)
        {
            result = CanExecuteFunction(parameter);
        }
        return result;
    }

    public void Execute(object parameter)
    {
        if (ExecuteFunction != null)
        {
            ExecuteFunction(parameter);
        }
    }

    public void OnCanExecuteChanged()
    {
        if (CanExecuteChanged != null)
        {
            CanExecuteChanged(this, new EventArgs());
        }
    }
}

ViewModel

Then I implemented the functionality in the ViewModel. Notice that:

  • The ViewModel truly represents the business. Its public members are: GreetingMessage, PersonName and GreetCommand.
  • The code implementation for the GreetExecute function is very simple, it reads/writes properties of the ViewModel and is not aware of controls on the View

Here is the code:

public class MainPageVM : BaseVM
{
    public MainPageVM()
    {
        InitializeCommands();
    }

    private string m_GreetingMessage = "Hello World!";
    public string GreetingMessage
    {
        get
        {
            return m_GreetingMessage;
        }
        set
        {
            if(value != m_GreetingMessage)
            {
                m_GreetingMessage = value;
                OnPropertyChanged();
            }
        }
    }

    private string m_PersonName = string.Empty;
    public string PersonName
    {
        get
        {
            return m_PersonName;
        }
        set
        {
            if (value != m_PersonName)
            {
                m_PersonName = value;
                OnPropertyChanged();
            }
        }
    }

    public VMCommand GreetCommand { get; set; }
    private void InitializeCommands()
    {
        GreetCommand = new VMCommand(GreetExecute);
    }

    private void GreetExecute(object o)
    {
        string greet = "Hello World!";
        if (!string.IsNullOrEmpty(PersonName))
        {
            greet = string.Format("Hello {0}!", PersonName);
        }
        GreetingMessage = greet;
    }

}

View

Then I wrote the XAML View. Notice that:

  1. We declare a ViewModel as a resource that we instantiate as the DataContext of the top Grid. This is how we access the ViewModel.
  2. The controls do not need to be named, because they are not referenced anywhere in the code.
  3. The Text properties of controls are bound to the ViewModel members. Because the ViewModel derives from BaseVM, the content of Text will change whenever the content of the ViewModel property changes. Also when the TextBox looses focus, it will automatically update the view model.
  4. Also there is no event handler on the Button instead there is a binding to the Command on the ViewModel.

Here is the code:

<Page
    x:Class="HelloWorld.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:HelloWorld"
    xmlns:vm="using:HelloWorld.VM"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    
    mc:Ignorable="d">
    <Page.Resources>
        <vm:MainPageVM x:Key="PageViewModel" />
    </Page.Resources>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" DataContext="{StaticResource PageViewModel}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="51"/>
            <RowDefinition Height="51"/>
            <RowDefinition Height="51"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="1" Text="{Binding GreetingMessage}" HorizontalAlignment="Center"/>
        <TextBox Grid.Row="2" Text="{Binding PersonName, Mode=TwoWay}" HorizontalAlignment="Center" Margin="5" Width="150"/>
        <Button Grid.Row="3" Content="Greet" Command="{Binding GreetCommand}" HorizontalAlignment="Center" />
    </Grid>
</Page>

Testing

Now that our application is written, we will build and test it. First I tested it on my local machine. Then switched to Remote Debugging to test it on the Raspberry Pi.

Conclusion

MVVM is a very powerful model for writing WPF application. I am glad to see that the Windows 10 IoT Core application can support full WPF application written in MVVM.

This means we can create much more evolved and complicated applications more easily.

, , , , ,

Leave a comment