Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Sharing data among Windows Forms

0.00/5 (No votes)
17 May 2012 1  
In a Windows application, passing values from a secondary form to the main form or share data among several applications (.NET application).

Introduction

In this article:

  • Reason for sharing data
  • How to do it?

After this article, you will be able to:

  • Pass values from a secondary form to the main form (Windows application)
  • Share data among several applications (.NET Remoting)

The goals mentioned above are in C#, without storing data!

Why share?

It sometimes comes up that data is entered from a WinForm and it's being used by other forms. What I mean is, in a simple position, you click a button on an Invoice form to open SelectCustomerForm, and then you will select a customer and close the form, therefore, the selected customer will be available and ready to use in the main form. And sometimes, it's not as simple as all that. For example, there is a need for showing some details, as soon as a name is selected from a ListBox of another form. So it is integral to have some solutions for sharing data amongst forms.

What are the solutions?

1. After closing a form, there is a need for accessing a selected object

Given there is a TextBox on EnterNameForm, a user will be able to enter a name by using it. You want to call the ShowDialog() method of a form then select a name. If you close the form, how can you access the name?

EnterNameForm enterNameForm = new EnterNameForm();
enterNameForm.ShowDialog();

One of the best courses of action I think would be to define a Property behind EnterNameForm:

public string EnteredName { get; set; }

In the FormClosed event, assign the name to the EnteredName property:

private void EnterNameForm_FormClosed(object sender, FormClosedEventArgs e)
{ 
   this.EnteredName = !string.IsNullOrEmpty(this.NameText.Text.Trim())?this.NameText.Text.Trim(): string.Empty;
}

After we close the form, the public properties are still available.

EnterNameForm enterNameForm = new EnterNameForm();
this.AddOwnedForm(enterNameForm);
enterNameForm.ShowDialog();

if (!string.IsNullOrEmpty(enterNameForm.EnteredName))
    HelloLable.Text = string.Format("Hello, {0}", enterNameForm.EnteredName);

2. There is the need for accessing a selected object as soon as it's selected:

Suppose there is a ListBox called Person on a form PersonList. It's shown by Show(). You know it is different from the previous situation (ShowDialog). Because there is not any grace to closing the form and the previous solution doesn't work!

Plan1

One solution using a delegate. It'll be able to pass the object after handling the event.

So my advice would be to use the delegate behind the PersonList form:

public delegate void ItemChangedHandler(Person sender);
public ItemChangedHandler ItemChanged;

And after that, select the object, and it's ready to use:

private void PersonListBox_SelectedIndexChanged(object sender, EventArgs e)
{
    if (ItemChanged != null)
       ItemChanged((Person)(((ListBox)sender).SelectedItem));
}

Now, behind the main form:

PersonList personList = new PersonList();
this.AddOwnedForm(personList);
personList.ItemChanged += new PersonList.ItemChangedHandler(ShowItem);

SetPersonListPosition(personList);
personList.Show();

ShowItem is a developer defined method that is able to put the object in the main form:

To exemplify what I mean, let's take this:

private void ShowItem(Person person)
{
   this.ChristianNameText.Text = person.ChristianName;
   this.SurnameText.Text = person.Surname;

   //...
}

To summarize, a delegate is capable of passing data, as soon as data changes.

3. There is the need for centralizing some data from several forms:

I'm convinced that a simple approach is static fields/properties. Why not?

If I may say so, it's clear that a few "opposed to static" fastidious will show up and flame me saying that:

"Freeze, put yer hands up where I can see 'em. Static? Huh?! Huh?! ..." And blah blah blah !

As a matter of fact, developers don't have to avoid static fields/properties occasionally, especially in a "Windows Application". For instance I can remind you of the "Singleton Pattern" and its public static property. Opponents believe "It can introduce problems when using multithreading, because it is not thread-safe". OK, granted, but first, are you going to use "Thread", every where, every time? (I don't think so). Secondly, there are various approaches to solving this problem. For example, look at these links: this and this. The important thing is use a static property in multiple threads, with utmost care!

Let's return to our muttons:

What could we do if we need some counters for all forms? Isn't a static Dictionary a mint solution?

public class Data
{
    public static Dictionary<string, int> Counter = new Dictionary<long, string>();
    public static void AddToCounter(string name)
    {
         int addNumber = 1;

         if (Counter.Keys.Contains(name))
             addNumber += Counter[name];

         Counter[name] = addNumber;
    }
}

And it's ready to work in the Load event of every form:

private void MyForm_Load(object sender, EventArgs e)
{
    Data.AddToCounter(this.Name);
    CounterLable.Text = Data.Counter[this.Name].ToString();
}

For more information, look at the source code file.

4. There is a need for sharing data among several applications

Passing data between two forms is not so hard in a single project, but what about several applications? Let's look at this scenario:

There is a defined customer (John Smith) at an accounting application, and then it's going to send him an SMS by using another application. How can the customer be shared between two separate projects?

Various solutions are available, for example: Connecting to a single database, socket programming, SOA, MemoryMappedFile, and the like.

I like to proceed to the ".Net Remoting" subject and "How to pass data from a server to client?" It's a generic and simple approach, not specific for a Windows application.

Suppose we have an instance of Person in a Windows App that is going to pass to another Console application:

The first necessary step is a reference to System.Runtime.Remoting, besides creating a class for Remoting and it inheriting from MarshalByRefObject. This is an important thing to do. I created an example class by using a "Class Library Solution":

public class RemotingObject: MarshalByRefObject
{
   public Person MyPerson { get; set; }

   public Person Process(long clientID)
   {
      Person result = new Person();

      if (DataSent != null)
         DataSent(clientID, ref result); 

      return result;
   }

   public delegate void SendDataHandler(long clientID, ref Person result);
   public static SendDataHandler DataSent;
}

The delegate enables this class (an instance of the class) to act like an intermediary in passing data, between the server and client. It has two parameters, clientID and result. clientID is passed from clients to prevent the server from broadcasting and result is passed from the server to the specific client. (The client has its ID = 1 in this program.)

Our Windows app is assumed as a server. In the constructor of the form, use this code:

channel = new TcpChannel(8080);

//Registering remote objects...;
RemotingConfiguration.ApplicationName = "Processor";
RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObject), 
                                          "MyURI", 
                                                  WellKnownObjectMode.Singleton);

8080 is the port number, you can choose something else!

Personally, I consider putting a CheckBox on the form for sharing or unsharing data.

private void ShareCheckBox_CheckedChanged(object sender, EventArgs e)
{
   if (!this.ShareCheckBox.Checked)
   {
      //Closing connections...;
      ChannelServices.UnregisterChannel(channel);
      RemotingObject.DataSent -= new RemotingObject.SendDataHandler(Method);
      return;
   }

   RemotingObject.DataSent += new RemotingObject.SendDataHandler(Method);

   //Registering channel... 
   ChannelServices.RegisterChannel(channel, false);
}

And a method for handling the DataSent event.

public void Method(long clientID, ref Person person)
{
   if (clientID != 1)
      return;

   person = selectedPerson != null ? selectedPerson : new Person();
   // To prevent exception at client.
}

OK, let's leave the server.

If you follow my advice, for testing the approach, afterwards create a Console application (client); pay attention to the following code:

static void Main(string[] args)
{
   long clientId = 1;  //You can change it or create a dynamic one for it.

   Console.WriteLine("Starting Client {0}", clientId);
   RemotingConfiguration.RegisterWellKnownClientType(typeof(RemotingObject),
                                        "tcp://localhost:8080/Processor/MyURI");

   RemotingObject instance = new RemotingObject();

   long bufferedID = 0;
   string prompt;

   while (true)
   {

      Console.Write("Prompt: ");
      prompt = Console.ReadLine().Trim().ToUpper();
      switch (prompt)
      {
          case "Q"://Quit the program
              return;


          case "N":
              try  // tring to get person from the server.

              {

                    Person result = instance.Process(clientId);
                    if (result != null && result.ID != bufferedID)
                         Console.WriteLine("{0} {1}", result.ChristianName, result.Surname);

                         bufferedID = result.ID;
              }
              catch (Exception ex)     

              // For example: If you run the client before the server.
             {
                        Console.WriteLine(ex.Message);
             }
             Console.WriteLine();
             break;
          default:
               //Send a message to server
               instance.Send(clientId, prompt);
               break;
         }
     }
}

On creating an instance of RemotingObject and calling the Process method, an event occurs at the server. The result of the method is a desired shared object.

Web applications are compelled to request for responses, but the rest of the applications doesn't have to be overtime work. Perhaps it's not a good idea that clients request by a loop! The goal was demonstrating how to share data. But you may use WellKnownObjectMode.SingleCall instead of WellKnownObjectMode.Singleton, if you don't want a loop.

Briefly, there are very variegated plans to share data among applications; my intention is to compile a simple pamphlet on "Passing Data by using .NET Remoting". In my own case, I haven't seen any simple articles for it. Yet!

Is there a way to send a message back to the server from the client?

The answer is yes.

Suffice it to say that, it needs more code and maybe a Control.

I prefer a ListBox to show messages in the server. I also plan to add the code to the RemotingObject class:

public void Send(long clientID, string message)
{
   if(SendMessage != null)
      SendMessage(clientID, message);
}

public delegate void SendDataHandler(long clientID, ref Person result);
public static SendDataHandler DataSent;

The client can send a message by using this code:

instance.Send(clientId, prompt);

And in the server:

private void ShareCheckBox_CheckedChanged(object sender, EventArgs e){
      //...
      RemotingObject.SendMessage += 
                  new RemotingObject.SendMessageHandler(MessageRecievedMethod);

      //...
}

private void MessageRecievedMethod(long clientID, string message)
{
   string myMessage = message;

   if (MessageList.InvokeRequired)//Invokerequred is because of different threads
   {
      MessageList.Invoke(new MethodInvoker(
         delegate { MessageList.Items.Add(
           string.Format("Client {0}: {1}", clientID, myMessage)); }));
   }
}

Now, the user is able to send a message from the client to the server. As easy as typing a message and pressing the Enter key to send.

The user can also use these commands from the client:

Q: Quit the program. N: Get the selected name.

Consequently

Well, several methods have been provided for allowing us to share and pass data among forms or applications. And some of them work the smartest. For example: WCF (Windows Communication Foundation), as Microsoft says, provides a unified programming model for rapidly building service-oriented applications that communicate across the web and the enterprise. Of course, we should be looking for the best, I granted it in a sense, but there is no reason why other solutions should be rejected.

Perhaps you'd care to download and look at the source code. I also insist you run the demo and send me your suggestions to improve the article.

History

OK, Collin Jasnoch had an idea about implementing INotifyPropertyChanged instead of delegate in solution 2 (need for accessing the selected object as soon as it's selected). He's right and I think that's a better (standard) idea. By the way I have another article where I used INotifyPropertyChanged. It's about "MVVM (Model-View-ViewModel) Pattern For Windows Form Applications, using C#" and the INotifyPropertyChanged interface is the main thing in that pattern.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here