---
date: 2025-01-18
---

# Universal robot arm as .NET WPF application

:::{warning}
Update 2025-11: I noticed that controlling individual robot movements using individual C# functions is not reliable and also does not reflect the reality how UR robots are programmed. I use URScript for the robot program and send it via simple TCP/IP functions to the robot, which I explain here:

<https://industrial-programming.aydos.de/week08/item-sorter-robot-real.html>
:::

:::{card} Background
[Previously](./universal-robot-arm-with-gripper-example-code.md), we created an app that picks up and releases an object. The application we created in the previous post focuses on the usage of the class `UniversalRobot` using *top-level statements*. Moreover it uses a console, which may be not convenient to use compared to a graphical user application.
:::

:::{image} universalrobot-control-csharp-example/universalrobot-control-wpf.png
:::
In the following we will setup a graphical WPF (Windows Presentation Foundation)-based application and replace the console with a window. WPF supports you to build a graphical user interface.

## Creating the C# project

1. Click on `Create a new project`. A list of project templates will be displayed.
1. Use the search bar and search for `wpf`.
1. Click on `WPF Application` that is for `C#` and then `Next`.
1. Name your project, e.g., `RobotTestGraphical` then click `Next`. `Additional information` window will open up asking e.g., for which .NET framework to use.
1. Leave default settings and click on `Create`. The IDE window will show up with `MainWindow.xaml` and `MainWindow.xaml.cs` files open. The first file will have the focus.
1. Build your project by pressing `F5`. A white window should pop up.

   🎉 You have a working program!
1. Close your program by closing the window. Now you should be back on `MainWindow.xaml`.

## Example application that integrates our previous robot control logic

1. Install `UniversalRobots` library using the NuGet package manager as in the previous post.
1. If you only want to browse the files to copy some code, browse the code [on the repository](https://gitlab.com/fpga-lab/universalrobot-control-csharp-example). Otherwise:

   You can directly download the files here:

   - {download}`universalrobot-control-csharp-example/UniversalRobot.cs`
   - {download}`universalrobot-control-csharp-example/gui/RobotProgram.cs`
   - {download}`universalrobot-control-csharp-example/gui/MainWindow.xaml.cs`
   - {download}`universalrobot-control-csharp-example/gui/MainWindow.xaml`

   After downloading copy the files and paste them into your project.
1. Build your project. You should see the user interface.

## Ideas used in this example project

### Separation of concerns

In a graphical app, we have a graphical user interface which can grow in complexity. Complexity is prune to errors, so we should separate responsibilities or concerns if a component of our program grows.
   The WPF framework template already enforces some separation of concerns by its project structure. `App` and `MainWindow` are separate. `App` should be responsible for the *business logic* and `MainWindow` for presenting information to the user and interaction.

Due to the low complexity of this example, I did not try to separate the business logic from the presentation logic completely. This step requires defining clear interfaces between these components, which is not the primary goal when creating a simple prototype.

Nevertheless, as an example for separation of concerns, I created a separate class for the robot command sequences called `RobotProgram`.

`MainWindow` instantiates `UniversalRobot` and `RobotProgram` and `RobotProgram` uses `UniversalRobot` to send commands to the robot.

Another aspect enforced by WPF is the separation of graphical structure and the functions-related to the interaction. `xaml` file is based on XML and is similar to HTML that you may be already familiar with. The `xaml` files describes the structure of a window and the events associated with them. An example event is `Click` on a button. For example `<Button ... Click="buttonClicked" ...` will execute the function `buttonClicked` whenever the user clicks a button. The function `buttonClicked` must be implemented in the corresponding C# code `MainWindow.xaml.cs`

### Use of events

In a GUI application, many back-end (business logic) components may want to communicate to the GUI. Instead of using a GUI function like `printThisOnTheGUI`, they can send strings to the *room* (metaphorically) and interested parties can receive them. `RobotProgram` uses `EventHandler` to do this. `MainWindow` listens to these events and prints them on the log screen

### Use of asynchronous functions

Our robot program can take many seconds. If we activate the sequence directly using a button, then the GUI must wait until the program completes and run its GUI-related functions like interaction with the user. This leads to a non-responsive GUI. We typically say *GUI freezed*.

To avoid this, we should create subprograms that run separately from the GUI program. An example is:

:::{literalinclude} universalrobot-control-csharp-example/gui/MainWindow.xaml.cs
:dedent:
:language: csharp
:start-at: StartClicked
:end-at: }
:::

### Use of columns and rows

Columns and rows in a grid can structure the elements in a convenient way. Example:

:::{literalinclude} universalrobot-control-csharp-example/gui/MainWindow.xaml
:dedent:
:language: xml
:start-at: <Grid>
:end-at: </Grid>
:::

First we declare each row and column available on our grid. The `*` notation allows describing the size of elements relative to each other. For example, the element with `Width="10*"` will be 10 times larger than an element with `*`.

`Grid.RowSpan` allows an element to span over many rows.

### Use of `IsEnabled` to enable or disable GUI elements

The `xaml` files describes the default state of the GUI elements:

:::{literalinclude} universalrobot-control-csharp-example/gui/MainWindow.xaml
:dedent:
:language: xml
:start-at: IsEnabled
:end-before: <
:::

This information can be changed during runtime of the program, which is described in C# code.

:::{literalinclude} universalrobot-control-csharp-example/gui/MainWindow.xaml.cs
:dedent:
:language: xml
:start-at: DisconnectClicked
:end-before: }
:::

### Persisting settings

Settings like IP and license data should be persisted when the app is closed and opened. This can be implemented by creating a settings file and loading it when the program starts.

### Using a for loop to repeat similar robot sequences

Loop is a superpower of a computer. If we have similar sequences, we can repeat these using a loop:

:::{literalinclude} universalrobot-control-csharp-example/gui/RobotProgram.cs
:dedent:
:language: csharp
:start-at: for (int
:end-before: Log
:::

### Creating functions and using meaningful variable names to make the code more readable

If you use `movej` most of the time with another function that checks whether the position is reached, then we could pack these two function into a single function.

:::{literalinclude} universalrobot-control-csharp-example/UniversalRobot.cs
:dedent:
:language: csharp
:start-at: public void MoveJ
:end-before: }
:::

Moreover, using good variable names, e.g.: 

:::{literalinclude} universalrobot-control-csharp-example/gui/RobotProgram.cs
:dedent:
:language: csharp
:start-at: movingOverPose
:end-before: Log
:::

These will make your robot control sequence more readable.




<!--
## Implementing the interaction between the user and the program logic

1. `MainWindow.xaml` describes the interactive elements. Let us add a component that can print us program messages.

    On the left-most vertical bar, click on `Toolbox`. Click on the `Search Toolbox` placeholder in the `Toolbox` search bar.
1. Search for `textblock` and double-click on it. A text block named `TextBlock` will be placed that fills the `MainWindow`. Moreover, a new line starting with `<TextBlock ...` will be placed in the the source code of `MainWindow.xaml`. You see that both graphical and text editor are synchronized.
1. On the graphical editor, right-click on the `TextBlock` and click on `Properties`. On the right-side a `Properties` window should open.
1. The `Name` field should contain `<No Name>`. Change it to `Logs`. You will maybe notice that the XML code in the text editor will be changed too.
1. Copy `UniversalRobot.cs` from the previous post and paste it into the `Solution Explorer` by hovering over the white space and pressing <kbd>Ctrl</kbd><kbd>v</kbd>. Now, the library should be part of your project.

1. On the right side, on the `Solution Explorer`, show the components of `App.xaml` by clicking on the triangle on the left side of `App.xaml`. Now, `App.xaml.cs` should be visible.
1. Double click on `App.xaml.cs`.

Now we are ready to integrate our robot control sequence from the previous post to the user interface (UI). Let us 
-->
