Hello World: A simple example
'Hello, World' is a simple groupware program that uses several basic features of the shared dictionary. The source/executable is included in the example downloads.
Read this example carefully and make sure you understand it. While it may seem a bit complicated for a 'hello world' program, it is actually doing some pretty amazing things for you. Future examples will build on these concepts.
We should also note that this program is an example of a simple distributed model-view controller (dMVC) pattern. The shared dictionary contains the distributed model. Each program has a controller that updates the model (in this case via a button press), and a view that is updated as a result to changes in the model (in this case via subscriptions that update the text in the button).
Watch this: The Hello World Movie
This example shows Hello World in Action. Watch it, and then try executing the example yourself. To summarize the steps illustrated in the movie:
1. Start a Server (the .Networking Dictionary Monitor)
- The first program that uses a sharedDictionary running at a tcp address automatically becomes a server. We will use the .Networking Dictionary Monitor for this purpose.
- From the Start menu, select All Programs/GroupLab/Networking/DictionaryMonitor.
- From this monitor, select the open button to open the dictionary at the default URL location tcp://localhost:test.
2. Execute Several Instances of the HelloWorldExample Program
- Go to the bin/debug folder of the HelloWorldExample, and run several instances of HelloWorld.exe. These will automatically connected to the server (the Dictionary Monitor) running at tcp://localhost:test.
- Enter different names in each text field to represent different people (where it says 'Type your name here').
- Click on one of the buttons. It immediately changes the button text in all instances to say the name of the person who said hello. The dictionary monitor will also show that it contains a key (/hello), and a value (e.g., "Fred says hello!") of the type System.String.
3. Create new instances / destroy old instances of the Hello World program
- The button label of a new instance will automatically update itself to reflect the current contents of the shared dictionary.
- You can kill any instance without affecting the others.
- If you kill the server (the dictionary monitor), the instances will detect that the server no longer exists and will disable the button.
Explaining the source code.
Note. While this example uses WPF, the basic code could be used to instrument a standard GDI C# interface.
References and Using
- In the HelloWorld project, include a reference to GroupLab.Networking normally located in C:/Program Files/GroupLab/Networking/bin. You can, of course, copy the entire bin folder to a place that is more convenient for you (e.g. in the project root directory).
- In the Using section, add:
using System.Windows.Threading; // needed for UI threading
The XAML
The XAML file is a simple interface description that just configures the various labels, buttons, textBoxes and event handlers. This is just standard WPF.
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Hello World Example" Height="131" Width="307">
<Grid>
<Button Content="Click me" Height="45" HorizontalAlignment="Left" Margin="4,4,0,0"
Name="button1" VerticalAlignment="Top" Width="276" DataContext="{Binding}" Click="button1_Click" />
<Label Content="Name:" Height="24" HorizontalAlignment="Left" Margin="4,55,0,0"
Name="label1" VerticalAlignment="Top" Width="60" />
<TextBox Height="29" HorizontalAlignment="Left" Margin="56,55,0,0" Name="textBox1" VerticalAlignment="Top" Width="223" Text="Anonymous" />
</Grid>
</Window>
Declarations
The declarations consist of:
- an instance of a shared dictionary (which handles all client/server connections)
- an instance of a subscription, where we subscribe to particular data in the shared dictionary
- a KEY constant (a regular expression string) that we will use to subscribe, set and get values in the shared dictionary
- a URL that defines the Url location of the shared dictionary server
Initialization
Initialization consists of a few major steps.
- Configure the shared dictionary
- We provide the Url of the server
- We create an Opened event handler that is triggered when the dictionary is completely opened (i.e., connections are successfully made to the server and all subscribed-to data has been sent)
- We create a Disconnected event handler that is triggered when the connection to the server is lost
- Configure the subscriptions
- The BeginInit' and EndInit'' block make sure that all subscription events are handled as a single sequence.
- We set the subscription to the shared dictionary
- We set the subscription pattern to the pattern of interest (in this case, it is the KEY defined as '/hello'). Note that this could just be a regular expression, i.e., subscribing to '/*' will subscribe to everything.
- We create a callback that will notify us of any changes to this key/value pair
- We open the Shared Dictionary.
- Important: Because the subscriptions have been set before we opened it, all data currently in the shared dictionary will automatically be sent to this process (and fire the notification callback) before the Opened Notification is triggered.
- We also do some User Interface stuff. In this example, we disabled the button that sets a value in the shared dictionary (as no connection has been established).
{
InitializeComponent();
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate()
{
button1.IsEnabled = false;
}));
this.shDict.Url = URL;
this.shDict.Opened += new EventHandler(shDict_Opened);
this.shDict.Disconnected += new EventHandler<DisconnectedEventArgs>(shDict_Disconnected);
this.subscription.BeginInit();
this.subscription.Dictionary = this.shDict;
this.subscription.Pattern = KEY;
this.subscription.Notified += new SubscriptionEventHandler(subscription_Notified);
this.subscription.EndInit();
this.shDict.Open();
}
Shared Dictionary Callbacks
Two callbacks are used. The Opened and Disconnected callback simply enables / disables the button depending on whether a dictionary connection has been established/terminated. Note that the Opened event is triggered after the subscribed-to data is sent to this client and the subscription callbacks triggered.
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate()
{
button1.IsEnabled = true;
}));
}
// Enable the button now that we are connected to the shared dictionary
void shDict_Disconnected(object sender, DisconnectedEventArgs e)
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate()
{
button1.IsEnabled = false;
}));
}
Subscription Callback
The subscription callback is invoked whenever the subscribed-to key has been added or deleted, or its value changed. In this case, we just get the value of the key and set the button contents to that value.
{
string value = (e.Reason == SubscriptionNotification.Remove) ? "Click me" : (string)this.shDict.GetEntry(KEY).Value;
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate()
{
button1.Content = value.ToString();
}));
}
Button Callback
Whenever the user clicks the button, it retrieves the name from the textbox, composes a message, and sets the Key to that message.
{
if (shDict.Status == ConnectionStatus.Opened)
{
this.shDict[KEY] = this.textBox1.Text + " says Hello!";
}
}
Variations
- This example illustrates all instances running on the same machine. Instead, try running the server on a different machine. Change the Url in the code to point to that location.
- Replace the button with a text box, and use that as a chat transcript that collects messages sent by people.
- Try extending the interface to add and subscribe to other key/value pairs, e.g., name, text_transcript, so that you can identify the person who sent a chat message
Last modified: October, 2010