CB Media Items: Hello World Example
Developing Community Bar Media Items...
Before starting this tutorial make sure that you have:
- Downloaded and installed Community Bar from the Community Bar page.
- Downloaded the Media Item Test Environment from the main Developing Community Bar Media Items page.
- Read the general overview about how to develop media items at Developing Community Bar Media Items
You can download the complete code for the Hello World Media Item example. To view this code in Visual Studio, unzip it to the MediaItemTester directory (it will overwrite your ItemTemplate directory).
The purpose is to create a simple Media Item as shown below (in the Media Item Tester):

1) Open the MediaItemTester.sln in Visual Studio.
2) Open the code view for ItemTemplate.cs in the ItemTemplate project.
3) Change the public name of the item to "Hello World" in the item name attribute. This line is found just above the class definition.
public class ItemTemplate : SideItem.IItem
{
...
4) Now we need to set up the shared data structure for this item. We need an ordered sequence of messages labelled with the sender's name. It seems most appropriate to keep this in a Vector of Maps (using shared dictionary data structure types): <item-key>/messages = Vector(Map("sender": sender-name, "message": message-content), Map("sender": sender-name, "message": message-content), ... )
- Add a variable at the top of the class for the subscription to this key:
GroupLab.Networking.Subscription msgSub;
- Initialise the subscription in the StartItem() method.
- When you type in the this.msgSub.Notified line, you will get a number of "Press TAB to Insert" shortcuts. Go along with these, including the one to create the callback method. Don't forget to come back and do the EndInit() method call though. For this example we only want to update when something is added, so we check for that first. The content will have to wait until we implement some of the Tile though. We also know that the data will be a Vector element and it will be a Map.
{
if(GroupLab.Networking.SubscriptionNotification.Add == e.Reason)
{
if(e.HasIndex)
{
// e is a Vector element
GroupLab.Networking.Map entry = e.Value as GroupLab.Networking.Map;
// update the tile
}
}
}
- Make sure that the subscription is cleaned up when the item is deleted. Change the StopItem() method to read:
{
this.msgSub.Notified -= new SubscriptionEventHandler(this.msgSub_Notified);
}
- We need a couple of methods to provide information about our messages. AllEntries() provides a list of all the people that have sent messages, Message constructs the message that is being sent, SendMessage() sends the message (puts it in the shared dictionary). As a general rule, it works out easier and cleaner to have all the shared dictionary access in this main class and get the tile, transients, and separates call these access methods.
{
get
{
GroupLab.Networking.SharedDictionary.Vector allEntries = new GroupLab.Networking.SharedDictionary.Vector();
// check that there is an entry
if(this.sd.Contains(this.key+"/messages"))
{
// get the Vector
allEntries = this.sd[this.key+"/messages"] as GroupLab.Networking.SharedDictionary.Vector;
}
return allEntries;
}
}
/// <summary>
/// Get the last message that was sent.
/// </summary>
/// <returns>Map ["sender": sender-name, "message": message-content]</returns>
public GroupLab.Networking.Map getLastEntry()
{
GroupLab.Networking.Map entry = new Map();
// check that the shared dictionary contains the vector
if(this.sd.Contains(this.key+"/messages"))
{
// get the last element
entry = this.sd[this.key+"/messages#-0"] as GroupLab.Networking.Map;
}
return entry;
}
///
/// Constructs the message that we are using.
///
public string Message
{
get
{
return "Hello " + this.ownerName + "!";
}
}
///
/// Send our message from this user.
///
public void SendMessage()
{
GroupLab.Networking.Map entry = new Map();
entry["sender"] = this.currentUserName;
entry["message"] = this.Message;
this.sd[this.key+"/messages#-0"] = entry;
}
Adding the Tile
The tile is the view that is seen in the bar. For the Hello World example the tile looks the same for both the master and the slave so we only have to create one.
1) Firstly, we have to change ItemTemplate to use just the one tile:
- Uncomment the variable definition for tile and comment out masterTile and slaveTile at the top of the class so that it now appears as:
// if you don't need a separate master/slave Tile, just remove the
//masterTile/SlaveTile declarations and use
public Tile tile; // a tile user control
//public MasterTile masterTile;
//public SlaveTile slaveTile;
- In the ItemTemplate() constructor change the initialisation to be for tile instead of masterTile and slaveTile so that it now looks like:
{
// if you don't need a separate master/slave Tile, just remove the
//masterTile/SlaveTile assignments and use
this.tile = new Tile(); // create a new tile user control
//this.masterTile = new MasterTile(this);
//this.slaveTile = new SlaveTile(this);
this.transients = new ArrayList(); // create a new array to hold the transients
}
- Finally we change the GetTile() method to return the single type of tile. It should now read:
public System.Windows.Forms.Control GetTile(int focus)
{
return this.tile;
}
2) Open the designer view of Tile.cs by double clicking on "Tile.cs" in the Solution Explorer pane.

3) Add a label to it from the Toolbox on the left. In the label properties pane on the bottom right, set:
* '(Name)' to 'msgLbl'; * 'Dock' to 'Fill' (select the middle block in the drop down); * 'Text' to nothing.

4) Change to the code view of the Tile by right clicking on the Tile design and selecting 'View Code'.
5) Implement the refresh method to change the text in the label so that we can update it from the main class.
{
this.msgLbl.Text = sender + " says \"" + msg +"\"";
}
6) Finally we have to initialise the Tile in the main class when it starts up and make sure it changes when the message is changed. Switch back to the ItemTemplate code.
- Start with an initial message in the tile. In the StartItem() method add a line to put in a default message.
if(entry.Count == 2)
{
this.tile.ChangeText(entry["sender"] as string, entry["message"] as string);
}
else
{
this.tile.ChangeText("nobody", "hello");
}
- Update the message in the tile when a new message is added. In the msgSub_Notified method: if we are receiving a Vector then we want the last element; if we are receiving a single element then we update with that element. The callback now looks like:
{
if(GroupLab.Networking.SubscriptionNotification.Add == e.Reason)
{
if(e.HasIndex)
{
// e is a Vector element
GroupLab.Networking.Map entry = e.Value as GroupLab.Networking.Map;
// update the tile
this.tile.ChangeText(entry["sender"] as string, entry["message"] as string);
}
}
}
7) Now the tile has been added. Run the tester by clicking on the play button at the top of the window. Check that the tile appears after you load the "Hello World" media item.
Adding the Master and Slave Transients
The Transient view is shown in the Tooltip Grandes. In this case we want to show different views in the master and slave views (slaves are able to send messages as well as view them). Fortunately a lot of the functionality is shared so we can use inheritance to save some repeated work. There are two properties of transients to note: (1) there could be more than one of them in existence at a time; and (2) they are created after the item is started (through the StartItem method). The effects of these two properties will become apparent.
We'll start with the stuff we can share and put it in the Transient.
1) Open the designer view of Transient by double clicking on it in the Solution Explorer.
2) Add a ListBox from the toolbox on the left to the Transient.
3) In the designer view of Transient add a ListBox to the control. Change the following properties:
* 'Name' to 'msgListBox' * 'Dock' to 'Fill'

4) Switch to the code view (right click on the user control and select 'View Code').
5) We need a method to update the content with new messages. Add the following method:
{
string fullMsg = sender + "says \"" + this.main.Message + "\"";
this.msgListBox.Items.Add(fullMsg);
}
5) The transient will be created in the middle of the item's life, so we need to initialise its state to show the right messages. In the constructor that gets a reference to "main", add in code to populate the transient. (You can delete the background colour assignment as well).
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
this.main = main;
// update with all the existing content
foreach(GroupLab.Networking.Map entry in this.main.AllEntries)
{
this.AddMessage(entry["sender"] as string, entry["message"] as string);
}
}
6) The next step is to make sure that the transients are updated when there is a change. Swap back to the code for ItemTemplate.cs. At the end of the method msgSub_Notified() add in code to update the Transients. The complete method should now look like this:
{
if(e.HasIndex)
{
// e is a Vector element
entry = e.Value as GroupLab.Networking.Map;
// update the tile
this.tile.ChangeText(entry["sender"] as string, entry["message"] as string);
// update the transients
foreach(Transient t in this.transients)
{
t.AddMessage(entry["sender"] as string, entry["message"] as string);
}
}
}
}
6) Switch to the SlaveTransient code view now. The behaviour of Transient is inherited by MasterTransient and SlaveTransient. MasterTransient only does what we have implemented so far so it is now finished. SlaveTransient does a little extra - it also sends messages, so we need to add in that funcionality now.
7) Add a button to send messages. The button will sit along the bottom of the control and a callback will send a message. We'll do this in code because the inherited listbox takes up all the space in the designer making it hard to click to place the button. Note use of the Place colour scheme again. The constructor (and button declaration) now should read:
public SlaveTransient(ItemTemplate main) : base(main)
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
this.sendButton = new Button();
this.sendButton.Text = "Say \""+main.Message+"\"";
this.sendButton.Dock = System.Windows.Forms.DockStyle.Bottom;
this.sendButton.SendToBack();
this.sendButton.Click += new EventHandler(this.sendButton_Click);
this.Controls.Add(this.sendButton);
}
8) The button callback simply sends a message using the SendMessage method in ItemTemplate.
{
this.main.SendMessage();
}
9) All the necessary calls to the transients in the main class have already been done. If you run the MediaItemTester again you can see that the Transients now work.
Final Note
Everything is now finished for the media item except for the Separate View content. Stepping through adding content to those forms wouldn't really demonstrate anything new about building media items. Hence this is left as an "exercise for the reader".