Tuesday, January 10, 2012

How to simply send simple instructions back to the User Interface thread in C#. SIMPLY.

After a few insane months where I had no time to give to this blog, I'm back with my first ever how-to.  All of the non-programming readers, which to date has also been all of the readers, may decide to switch off at this point.  Go ahead; I can't stop you, but let me preface the tutorial with a few reader-friendly notes.  If you just came here for the answer, there are helpful headings that you can scroll down for.


First, some Ranting
There are a lot of how-tos and posts on the internet on this subject, and it's little wonder, because almost none of them provide the pants-first-then-shoes-simple answer in a way that remotely approaches readability.  The answerers either assume that you're "one of them" and give the answer in jargon that they refuse to define, or they assume that you know nothing at all and so give you half an hour or more of reading material to get you up to speed with everything you could conceivably require on the subject, within which the answer you actually want is buried within layers and layers of context that universally defy the tried-and-true technique of scanning the crap you don't need for the one piece of information you do, just like this sentence.

The reasoning that I've seen for this behaviour is simple:  Either you're one of us, or you're not.  Either you already know the jargon and just need a fellow code-ninja to give you a hint, or you don't understand the jargon, in which case you need to LEARN SOMETHING, you ignorant buffoon.  Giving YOU the answer in plain language is like giving a katana to a three-year-old:  sure, it's possible to put that kind of power into your hands, but I'm not irresponsible enough to do it.

Poppycock.

There is a simple way to do this task, because it's a very common task.  The people who designed the UI (user interface) framework that you're trying to program with know that it's a common task, and they've made it really easy by not requiring you to know how to do it from scratch.


Here's the answer if you understand the question and are just looking for the magic words to use in C# 

Using GTK# Windows
It might be the same using GTK+, but I haven't tried.  The benefit of using GTK is that it should work on Windows, MacOS and Linux.

using Gtk;

/*...*/

Gtk.Application.Invoke(delegate
{
   /*code for the UI thread*/
});


Using Windows Forms
This is the most complex example, because Windows Forms are picky and need the delegate to be of System.Delegate type.  I don't know why.

using System.Windows.Forms;

/*...*/

public delegate void myDelegate();
public delegate void myDelegateWithParameters(int x);

public void FunctionA()
{
   this.Invoke(new myDelegate(this.FunctionX));

   int a = 0;
   this.Invoke(new myDelegateWithParameters(this.FunctionY),
                new object[] { a });
}

public void FunctionX()
{
   /*code for the UI thread*/
}

public void FunctionY(int x)
{
   /*code for the UI thread*/
}


Using Android Activities (with Mono for Android)
using Android.App

/*...*/

this.RunOnUiThread(delegate
{
   /*code for the UI thread*/
});


Further Questions
Maybe you're a coding newbie for whom that was a little dense. Here are some explanations for you, in approximate order of importance.


What is "this"?
You may have noticed in the code above that I used "this.something" in a few places.  "this" refers to where we currently are.  If you type in "this." in your development environment, you should get a list of everything that's possible in the current context.  It's a very, very useful feature.

After I found the Windows Forms example, I saw that "this.Invoke" was the key magical phrase that would fix everything.  So in my Android UI code I typed in "this." and then scrolled down the list looking for something likely.  "RunOnUiThread" was a pretty good bet, so I tried it.

Guess what?

IT DID EVERYTHING I NEEDED WITH ABSOLUTELY NO FUSS, ALL WITHOUT LECTURING ME ON HOW TO INITIALISE A THREADPOOL OR PROPERLY MAINTAIN A TASK QUEUE.

For this I was grateful, and a little upset that I'd spent two days googling for the answer without coming across it.  Not ONCE.  The lesson I learned was that if you think there's a key phrase that will be useful but you don't know what it is, try "this." and see what pops up.

In all of the examples, "this." can be removed and the code will still work.  I'm leaving it in there, though, so you can see how the code was made.  I for one wish to thank Guest_frankg* for unnecessarily leaving "this." in his own code snippet on a three-year-old forum post so I could follow his example.


Why are there delegates everywhere?
A delegate is something that acts like both a function and a variable.  That means you can take a delegate, which contains code like a function, and you can pass it to another function as if it were a variable.  If you're familiar with pointers to functions or callback routines, it's helpful to know that both of those things are essentially the same as delegates, otherwise just ignore this sentence.

In the Windows example, I created a delegate, gave it a name, and then pointed it towards an existing function.

In the Android and GTK# examples, I made a delegate and defined what it stood for right there on the spot, so I never needed to give it a name.  That's called an Anonymous Method.  Let's look at it again.

this.RunOnUiThread(delegate
{
   /*code for the UI thread*/
});

It looks convoluted with all the brackets and syntax everywhere, but it's actually fairly simple.  The delegate is a function, so it has braces that contain code.  But it's also behaving like a variable, so it sits inside the parentheses of the RunOnUiThread function.  So there's an example of a delegate behaving like a function and a variable at the same time.


Why would I want to send code back to the User Interface thread?
Threads allow programs to run code in parallel.  A common use of threads is to allow programs to run time-consuming code in the background while the user interface remains available and responsive to the user.  The background thread often needs to update the user interface on its progress.  It's not allowed to update the user interface directly, because it will conflict with the main UI thread, and that will cause the program to hang and/or crash.  Either way, it's bad.

So methods like Invoke(delegate) and RunOnUiThread(delegate) exist to automatically manage the UI thread for you.  When you give them a delegate, they will place that delegate on a queue that the main UI thread will get around to when it's good and ready.  Because the UI thread's entire existence consists of waiting around for things to respond to, it's pretty good at handling these requests in a timely manner.


Huzzah!  What could possibly go wrong?
Quite a lot, actually.  As in the rest of life, with great power comes great responsibility, and these are fairly powerful commands.  Using these functions means you're messing about with multithreaded applications, and they're a different beast to single-threaded applications.

You should research multithreading and events & delegates.  Events & delegates are fairly straightforward to use once you understand them, but with multithreading there are a lot of dos and don'ts, and unless you start with some idea of what's going on, you'll waste a lot of time trying to figure it out.

If you know enough about coding to get yourself into trouble, then hopefully this post is enough to help you make the code that you want to make.

If you're a veteran coder who sees a problem with anything in this post, then I'm sure you don't need me to encourage you to leave a comment about it.


If you're scared of programming you can start reading again now.
However, I do have a bit more ranting to do on the subject of programming tutorials. One of the reasons that they defy attempts to scan them for relevant details is that they are written in much the same way programs are written.

I've always thought that good programmers would also be good communicators, because they're used to declaring everything and leaving no room for assumptions. Computers need that, because they won't infer anything. Good coders know precisely what they have and have not said.

However, programmers are also used to having perfect attention from their audience. Computers read absolutely every character that's written and they don't forget any of it. A computer has no concept of how one thing that's said can be more or less relevant than anything else. Headings are not useful to them.

A person, of course, is different. They will tune out when you're boring them, and they'll forget about the little details you're needing them to remember. They need headings and they don't need you to explain every single detail. They need you to have a sense of perspective when you're writing and not waste their time with information that isn't important.

And if all of the information is important?  Break it down.  That's why you're the teacher.  They don't know how to break it down yet, which is why they're reading your tutorial.  Too many tutorials that I've seen are just walls of text and code without any identifiable entry-points for the average reader.

If you're going to write tutorials for coding, you need to know how to code, obviously.  What appears to be less obvious, though, is that you also need to know how to communicate with people, and that's a very different skill.


2 comments:

  1. This. This.

    ... I have no idea what's going on.

    ReplyDelete
  2. It... no, you can't just type it anywh... ugh.

    Here is a this.ball; perhaps you'd like to this.ball.bounce() it.

    ReplyDelete