Home / C# / Writing your first HMI project in C# – WPF

Writing your first HMI project in C# – WPF

Writing an Hmi with C# and WPF is quite easy, once that you know how to structure your project.
In this post i will make an example on how to write a simple Hmi (2 pages) that can access to a variable that get updated from another thread.

Structure of the project

The structure of the Hmi will be the following:
– Graphical User Interface: written in WPF using the PageSwitcher class.
– Plc variables: global static variables that can be read(-only) from all pages.
– Plc driver: multithreaded communication to avoid to block the graphic user interface thread.

Once you created your pages, you need to develop the communication driver for your plc, or to use an existing driver.
The communication driver is usually synchronized with the plc, this means that the driver sends a request to the plc and waits until the plc answers; the waiting time can be short or long, depending on the protocol and cable.
A synchronized communication can be a pain for your graphic user interface, because it will lock the pages for the most of the time; that’s why the communication has to be hosted on a secondary thread and plc-variables needs to be synchronized with the GUI thread.

Plc variables and communication thread

To write an Hmi and access to plc values i often use global static variables, that contains the values that i read from the plc.

Usually i declare a plc variable in this way:

public static int Count { get; private set; } 

The “private set” means that the variable is read-only outside the class, and this is logic because you can’t write a plc-variable without write it in the plc before.
To contain the variables and the communication thread you should use a static class.

public static class Plc
{
    static Thread t;
    
    static Plc()
    {
        // this is an example of static constructor, that i don't need in this example and i leave it empty.
    }

    public static void StartCommunication()
    {
        t = new Thread(new ThreadStart(CommunicationThread));
        t.Name = "PlcCommunication";
        t.Start();
    }
}

The communication thread is:

static readonly object _locker = new object();
private static void CommunicationThread()
{
    while (!StopCommunication)
    {
        lock (_locker) 
        {
             Count++;
             Thread.Sleep(500); //long elaboration
        }
    }
}

Access to plc-variables inside the pages

To access to plc-variables inside the hmi pages, just declare a timer for each page, with a callback that updates the GUI objects with the plc-variables.

DispatcherTimer timer = new DispatcherTimer(); 

public Page1()
{
    InitializeComponent();
    timer.Interval = TimeSpan.FromMilliseconds(100);
    timer.Tick += new EventHandler(timer_Tick);
    timer.Start();

    timer_Tick(null, null); //this will refresh the textboxes before you show the page
}

void timer_Tick(object sender, EventArgs e)
{
    txtCount.Text = Plc.Count.ToString();
}

Memory issues

There is a known memory leak with DispatcherTimer inside UserControls, because if it’s not stopped it will not release the UserControl and you will keep in RAM all the page that you visit.
The reference is here: http://geekswithblogs.net/dotnetrodent/archive/2009/11/05/136015.aspx
To avoid the memory leak remember to subscribe to the event UserControl_Unloaded:

<UserControl x:Class="HmiExample.Pages.Page1"
             ...
              Unloaded="UserControl_Unloaded">

and to stop the timer in the event handler:

     
private void UserControl_Unloaded(object sender, System.Windows.RoutedEventArgs e)
{
    timer.Stop();
}

Example for VS2010

This is a quick and dirty example, but it will get you started until you will master WPF and Datacontext – Databinding mechanism.
Download the example for Visual Studio 2010.

Share Button

11 comments

  1. Thanks Mesta

    I’m trying to use VS2012 and OPC UA.
    Check out my work at http://opcuaservicesforwpf.codeplex.com/

    Nice gauges!

  2. How to close all your application from page, because this.Close doesn’t work

    • You should use Application.Current.Shutdown(); to close your application.
      If you need to release some resources, like your tcp socket, you should use the event Closing of your MainWindow.

  3. And If I create TCP connection in your PLC class and timer, where should I close TCP connection and stop timer??

  4. i have read some example in http://www.mesta-automation.com
    but i don’t How do communicate over the ethernet from one CompactLogix to pc used C#. you can hepl me. thank you very much.

  5. Which is the method most suitable to write data to the PLC with your class.
    How I need interact with the thread to write?

    • It depends on the driver. If it is full duplex, you can read while you write (for example TCP/IP), but if it is half duplex (like RS-485), you need to synchronize the read with the write in your thread. In the second case, the write must have the highest priority.

  6. Hi,
    Thanks for providing the example.
    I’m new in developing such application. It would be very helpful if you could answer the following question:

    Communication between controller and application is established. I can see the data when I navigate to any page for first time, but when I navigate to other page and come back, the data get lost (In Labels, textbox etc)!
    I read about this problem on web, and it says ‘Data binding’ is the solution. I tried adding a class — : INotifyPropertyChanged,
    also I tried ‘Page.KeepAlive’, none of them works.
    Can anybody please reply if you’ve an answer- how to keep holding to values in Labels while navigating through pages?

    Thanks a ton.

Leave a Reply