Click here to Skip to main content
15,902,777 members
Articles / Programming Languages / C#

Sharing Data among Windows Forms

Rate me:
Please Sign up or sign in to vote.
4.88/5 (65 votes)
17 Apr 2012CPOL7 min read 138.6K   6.2K   152  
In Windows application, passing values from a secondary form to the main or share data among several applications .Net application.
This is an old version of the currently published article.

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 to share?

It sometimes comes up that data is entered from a winform and it's being used by other forms. What I mean, 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 of 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 solutions?

1. Afterwards closing a form, there is a need of accessing to the selected object.

Given, there is a "TextBox" on "EnterNameForm", so that 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 to the name?

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

One of the best course of action that I think, would be to define a "Property" behind of "EnterNameForm":

C#
public string EnteredName { get; set; }

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

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

Afterwards close a form, public properties of it, is so available.

C#
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 a need of accessing to the selected object as soon as it's selected:

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

Plan1

One of solutions is using "delegate". It'll be able to pass the object afterwards handle the event.

So, my advice would be to use delegate behind of "PersonList" form:

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

And afterwards select the object, it's ready to use:

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

Now, behind of the main form:

C#
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:

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

   //...
}

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

3. There is a need of centralizing some data from several forms:

I'm convinced that simple approach is "<code><code>static" fields/properties.

Why not?

If I may say so, it's critical clear that a few "opposed to static" fastidious, will show up and flay me 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 "Windows Application". For instance I can remind you of "Singleton Pattern" and it's public static property. Opponents believe "It can introduce problems when using multithreading, because it is not thread-safty". Ok, granted, but firstly, are you going to use "Thread", every where, every time? (I don't think so) Secondly, there are various approaches to solve this problem. For example look at the links, this and this. The most is that use 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?

C#
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 everyform:

C#
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 of 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:

There is a defined customer (John Smith) at an accounting application, and then it's going to send him a SMS by using something else application.

How can the customer be shared between two separated projects?

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

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

Supposing an instance of "Person" in a Windows App that is going to pass to another Console application:

Image 2

The first necessary step is a reference to "System.Runtime.Remoting", besides create a classfor acts of ramoting and it's inheriting from "MarshalByRefObject". This is an important thing to do. I created an example class by using a "Class Library Solution":

C#
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 "<span id="ArticleContent151"><span id="ArticleContent152"><span id="ArticleContent153"><span id="ArticleContent154"><span id="ArticleContent155">delegate</span></span></span></span></span>" enables this class (an instance of the class) to act like an intermediary in passing data, between server and client. It has two parameters, "clientID" and "result". You know, "clientID" is passing from clients to prevent the server for broadcasting and "result" is passing from the server to the specific client. (The client that its ID Number = 1 in this program)

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

C#
channel = new TcpChannel(8080);

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

"8080" is a port number, you can choose something else!

Personally, I consider putting a "<span id="ArticleContent116"><span id="ArticleContent117"><span id="ArticleContent118"><span id="ArticleContent119"><span id="ArticleContent120">CheckBox</span></span></span></span></span>" on the form for sharing or unsharing data.

C#
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 "DataSent" event.

C#
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), you'll pay attention to the following code:

C#
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"Process" method, an event occured at server. The result of the method is desired shared object.

Web applications are compelled to request for responses, but the rest of the applications don't have to 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 as for it. Yet!

Is there any 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 server. I also plan to add the code to RemotingObject class:

C#
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;

Client can send message by using this code:

C#
instance.Send(clientId, prompt);                        

And in the server:

C#
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, user is able to send message from cilent to server. As easy as typing a message and pressing Enter key to send.

User can also use these commands form 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 that it 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 insisted that run demo and send me your suggestions to improve the article.

History

Ok, Collin Jasnoch, had an idea about implementing INotifyPropertyChanged instead of delegate in solution2 (need of accessing to 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 that I used INotifyPropertyChanged in it. It's about "MVVM (Model-View-ViewModel) Pattern For Windows Form Applications, using C#" and INotifyPropertyChanged interface is the main thing in that pattern.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Iran (Islamic Republic of) Iran (Islamic Republic of)
Microsoft Certified Technology Specialist (MCTS)


"شاهین خورشیدنیا"


Nobody is perfect and neither am I, but I am trying to be more professional. In my humble opinion, you have to develop your skills, as long as you are responsible for developing even a tiny part of a sustainable software. That makes this job so special to me.

Comments and Discussions

Discussions on this specific version of this article. Add your comments on how to improve this article here. These comments will not be visible on the final published version of this article.