Overview
This is a step-by-step user guide that explains how to create a simple Windows Forms Application that uses the Coherence for .NET library.
General Instructions
Developing and configuring a Windows Forms Application that uses Coherence for .NET requires five basic steps:
- Create a Windows Application in Visual Studio 2005.
- Add a reference to the Coherence for .NET library.
- Create an App.config file.
- Configure the Coherence for .NET library.
- Create and design a Windows form.
- Implement the application.
Creating a Windows Application Project
To create a new Windows Application, follow these steps:
- Go to the File->New->Project... tab in Visual Studio 2005.
- In the New Project window choose the Visual C# project type and Windows Application template. Enter the name, location (full path where you want to store your application), and solution for your project.

- Click OK.
Visual Studio should have created the following files: Program.cs, Form1.cs and Form1.Designer.cs. The Solution Explorer in your project should now look like this:

Next, rename these files if you wish. In this example they have been renamed to ContactCacheClient.cs, ContactForm.cs, and ContactForm.Designer.cs respectively.
Add a Reference to the Coherence for .NET Library
To use the Coherence for .NET library in your .NET application, you must first add a reference to the Coherence.dll library.
Adding a reference to the Coherence.dll library:
- In your project go to Project->Add Reference... or right click on References in the Solution Explorer and choose Add Reference...
- In the Add Reference window that appears choose the Browse tab and find the Coherence.dll library on your file system.

- Click OK.
Creating an App.config File
To correctly configure the Coherence for .NET library, you must create an App.config XML file that contains the appropriate file names for each configuration file used by the library.
- Right-click the project in the Solution Explorer and choose the Add->New Item... tab.
- In the Add New Item window select the Application Configuration File.

- Click OK.
This is a sample of a valid App.config configuration file:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="tangosol-coherence" type="Tangosol.Util.CoherenceConfigHandler, Coherence"/>
</configSections>
<tangosol-coherence>
<cache-factory-config>coherence.xml</cache-factory-config>
<cache-config>cache-config.xml</cache-config>
<pof-config>pof-config.xml</pof-config>
</tangosol-coherence>
</configuration>
In the <configSections> you must specify a class that handles access to the Coherence for .NET configuration section.
Elements within the Coherence for .NET configuration section are:
- cache-factory-config - contains the path to a configuration descriptor used by the CacheFactory to configure the (ConfigurableCacheFactory and Logger) used by the CacheFactory.
- cache-config - contains the path to a cache configuration descriptor which contains the cache configuration described earlier (see Configuring Coherence*Extend on the Client). This cache configuration descriptor is used by the DefaultConfigurableCacheFactory.
- pof-config - contains the path to a configuration descriptor used by the ConfigurablePofContext to register custom types used by the application.
You can find a detailed description of each of these configuration files in the Coherence for .NET User Guide.
Creating Coherence for .NET Configuration Files
Here is an example of the coherence.xml configuration file:
<?xml version="1.0"?>
<coherence xmlns="http://schemas.tangosol.com/coherence">
<logging-config>
<destination>ContactCache.log</destination>
<severity-level>5</severity-level>
<message-format>{date} <{level}> (thread={thread}): {text}</message-format>
<character-limit>8192</character-limit>
</logging-config>
</coherence>
Here is an example of the cache-config.xml configuration file:
<?xml version="1.0"?>
<cache-config xmlns="http://schemas.tangosol.com/cache">
<caching-scheme-mapping>
<cache-mapping>
<cache-name>dist-contact-cache</cache-name>
<scheme-name>extend-direct</scheme-name>
</cache-mapping>
</caching-scheme-mapping>
<caching-schemes>
<remote-cache-scheme>
<scheme-name>extend-direct</scheme-name>
<service-name>ExtendTcpCacheService</service-name>
<initiator-config>
<tcp-initiator>
<remote-addresses>
<socket-address>
<address>localhost</address>
<port>9099</port>
</socket-address>
</remote-addresses>
</tcp-initiator>
<outgoing-message-handler>
<request-timeout>30s</request-timeout>
</outgoing-message-handler>
</initiator-config>
</remote-cache-scheme>
</caching-schemes>
</cache-config>
Here is an example of the pof-config.xml configuration file:
<?xml version="1.0"?>
<pof-config xmlns="http://schemas.tangosol.com/pof">
<user-type-list>
<include>assembly://Coherence/Tangosol.Config/coherence-pof-config.xml</include>
<user-type>
<type-id>1001</type-id>
<class-name>ContactCache.Windows.ContactInfo, ContactCacheClient</class-name>
</user-type>
</user-type-list>
</pof-config>
Having creating these configuration files, everything is now in place to connect to a Coherence cluster and perform all operations supported by Coherence for .NET.
Create and Design the Application
Next, you must add controls to your Windows form. This example shows you how to store objects into a INamedCache, read from the cache, query the cache, remove an item from the cache, and clear the cache. For this we're going to use buttons that will raise events when clicked, a couple of TextBox components for editing objects, and a DataGridView for displaying the current contents of a INamedCache. In this example we're going to work with just a ContactInfo user type, but a similar approach can be used with any other user defined type.
To add controls in your application follow these steps:
- Go to View->Toolbox.
- In the Toolbox window choose the controls you wish to use and drag them on the Windows form.
- For each control, right-click on it, choose Properties tab, and set the necessary properties.
When finished, your application should look like this:

Implementation
The first step in the implementation of the example Windows application is to create a ContactInfo class that implements the IPortableObject interface.
public class ContactInfo : IPortableObject
{
private string name;
private string street;
private string city;
private string state;
private string zip;
public ContactInfo()
{ }
public ContactInfo(string name, string street, string city, string state, string zip)
{
this.name = name;
this.street = street;
this.city = city;
this.state = state;
this.zip = zip;
}
public void ReadExternal(IPofReader reader)
{
name = reader.ReadString(0);
street = reader.ReadString(1);
city = reader.ReadString(2);
state = reader.ReadString(3);
zip = reader.ReadString(4);
}
public void WriteExternal(IPofWriter writer)
{
writer.WriteString(0, name);
writer.WriteString(1, street);
writer.WriteString(2, city);
writer.WriteString(3, state);
writer.WriteString(4, zip);
}
}
Before the application can start handling events, we must bind the DataGridView control with a data source object:
- In the Toolbox window choose the BindingSource object and drag it onto the form.
- Set its properties. Enter contactsBindingSource into the Name field and then set its data source by clicking the arrow button on the right end of the DataSource field. In the drop down window choose Add Project Data Source... and the Data Source Configuration Wizard will appear. Chose Object and find the ContactInfo class in your project.

- The final step is to bind the DataGridView control to the contactBindingSource. This is done by simply choosing the contactsBindingSource in the drop down window in the DataSource field of the DataGridView properties window:

Now we have bound contactsBindingSource to our DataGridView control and all further interaction with the data, including navigating, sorting, filtering, and updating, is accomplished with calls to the BindingSource component. We also need IFilter and CacheEventFilter fields to manage filtering as well as a WindowsFormsCacheListener field used to ensure that any event handling code that needs to run as a response to a cache event is executed on the UI thread. For this to work, we'll have to delgate methods for each cache event we're handling and then register a listener with the cache via the AddCacheListener() method. This is explained in more details in Special Considerations Regarding Windows Forms Applications. In the constructor, we will also obtain the INamedCache that we're using in the application via the CacheFactory.GetCache() static method and initialize the ComboBox used for choosing the search attribute.
private INamedCache cache;
private WindowsFormsCacheListener listener;
private IFilter filter;
private CacheEventFilter cacheEventFilter;
private string pattern;
public ContactForm()
{
listener = new WindowsFormsCacheListener(this);
listener.EntryInserted += new CacheEventHandler(AddRow);
listener.EntryUpdated += new CacheEventHandler(UpdateRow);
listener.EntryDeleted += new CacheEventHandler(DeleteRow);
cache = CacheFactory.GetCache("dist-contact-cache");
cache.AddCacheListener(listener);
InitializeComponent();
InitializeComboBox();
}
private void InitializeComboBox()
{
cmbAttribute.Items.Add("Name");
cmbAttribute.Items.Add("Street");
cmbAttribute.Items.Add("City");
cmbAttribute.Items.Add("State");
cmbAttribute.Items.Add("Zip");
cmbAttribute.SelectedIndex = 0;
}
As with any other Windows application, most of the remaining implementation has to do with event handling. Since each component in the Windows form can raise an event, event handlers must be created to handle each event. Event handlers in Visual Studio can be added to your application by following these steps:
- Right-click the Window component for which you'd like to implement an event handler and choose Properties.
- In the upper toolbar of the Properties window, select the lighting button and all events that the component can raise will be displayed.

- Choose the event you wish to handle and double-click it. Visual Studio will add the necessery code to your application to enable you to handle the event. Next, you must implement the empty event handler method.
Here is the event code in the sample Windows application:
private void ContactForm_Load(object sender, EventArgs e)
{
RefreshContactsGrid(true);
}
private void ContactForm_FormClosed(object sender, FormClosedEventArgs e)
{
cache.RemoveCacheListener(listener, cacheEventFilter);
}
private void addressDataGridView_CellEnter(object sender, DataGridViewCellEventArgs e)
{
DataGridViewCellCollection cells = addressDataGridView.CurrentRow.Cells;
txtName.Text = (string) cells[0].Value;
txtStreet.Text = (string) cells[1].Value;
txtCity.Text = (string) cells[2].Value;
txtState.Text = (string) cells[3].Value;
txtZip.Text = (string) cells[4].Value;
}
private void btnPut_Click(object sender, EventArgs e)
{
String name = txtName.Text;
ContactInfo contact = new ContactInfo(txtName.Text,
txtStreet.Text,
txtCity.Text,
txtState.Text,
txtZip.Text);
cache.Insert(name, contact);
}
private void btnRemove_Click(object sender, EventArgs e)
{
cache.Remove(txtName.Text);
ResetTextBoxes();
}
private void btnClear_Click(object sender, EventArgs e)
{
cache.RemoveCacheListener(listener, cacheEventFilter);
cache.Clear();
cache.AddCacheListener(listener, cacheEventFilter, false);
contactsBindingSource.Clear();
ResetTextBoxes();
}
private void btnRefresh_Click(object sender, EventArgs e)
{
string newPattern = txtPattern.Text;
string attribute = (string) cmbAttribute.SelectedItem;
if (!newPattern.Equals(pattern))
{
pattern = newPattern;
cache.RemoveCacheListener(listener, cacheEventFilter);
if (pattern != String.Empty)
{
IValueExtractor extractor = new ReflectionExtractor("get" + attribute);
filter = new LikeFilter(extractor, pattern, '\\', false);
cacheEventFilter = new CacheEventFilter(CacheEventFilter.CacheEventMask.All
| CacheEventFilter.CacheEventMask.UpdatedEntered
| CacheEventFilter.CacheEventMask.UpdatedLeft,
filter);
}
else
{
filter = null;
cacheEventFilter = null;
}
cache.AddCacheListener(listener, cacheEventFilter, false);
}
RefreshContactsGrid(true);
}
private void cmbAttribute_SelectedIndexChanged(object sender, EventArgs e)
{
pattern = "";
}
We also have to write cache event handlers, as delegated in constructor:
private void AddRow(object sender, CacheEventArgs args)
{
contactsBindingSource.Add(args.NewValue);
}
public void UpdateRow(object sender, CacheEventArgs args)
{
int index = contactsBindingSource.IndexOf(args.OldValue);
if (index < 0)
{
contactsBindingSource.Add(args.NewValue);
}
else
{
if (SatisfiesFilter(args.NewValue))
{
contactsBindingSource[index] = args.NewValue;
}
else
{
contactsBindingSource.RemoveAt(index);
}
}
}
public void DeleteRow(object sender, CacheEventArgs args)
{
contactsBindingSource.Remove(args.OldValue);
}
And here are helper methods used by the event handlers above:
private void ResetTextBoxes()
{
txtName.Text = "";
txtStreet.Text = "";
txtCity.Text = "";
txtState.Text = "";
txtZip.Text = "";
}
private void InitializeComboBox()
{
cmbAttribute.Items.Add("Name");
cmbAttribute.Items.Add("Street");
cmbAttribute.Items.Add("City");
cmbAttribute.Items.Add("State");
cmbAttribute.Items.Add("Zip");
cmbAttribute.SelectedIndex = 0;
}
private bool SatisfiesFilter(object obj)
{
IFilter clientFilter = new LikeFilter(new ReflectionExtractor((string) cmbAttribute.SelectedItem),
pattern, '\\', false);
return clientFilter.Evaluate(obj);
}
private void RefreshContactsGrid(bool updateContacts)
{
if (updateContacts)
{
RefreshContacts();
}
contactsBindingSource.ResetBindings(false);
}
private void RefreshContacts()
{
contactsBindingSource.Clear();
ICollection cacheEntries = (filter == null ? cache.Values : cache.GetEntries(filter));
foreach (object entry in cacheEntries)
{
if (entry is DictionaryEntry)
{
contactsBindingSource.Add(((DictionaryEntry) entry).Value);
}
else
{
contactsBindingSource.Add(entry);
}
}
}