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?
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
":
public string EnteredName { get; set; }
In "FormClosed
" event, assign the name to "EnteredName
" property:
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.
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!
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:
public delegate void ItemChangedHandler(Person sender);
public ItemChangedHandler ItemChanged;
And afterwards select the object, it's ready to use:
private void PersonListBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (ItemChanged != null)
ItemChanged((Person)(((ListBox)sender).SelectedItem));
}
Now, behind of 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, "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>st
atic" 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?
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:
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:
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":
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:
channel = new TcpChannel(8080);
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.
private void ShareCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (!this.ShareCheckBox.Checked)
{
ChannelServices.UnregisterChannel(channel);
RemotingObject.DataSent -= new RemotingObject.SendDataHandler(Method);
return;
}
RemotingObject.DataSent += new RemotingObject.SendDataHandler(Method);
ChannelServices.RegisterChannel(channel, false);
}
And a "Method
" for handling "DataSent
" event.
public void Method(long clientID, ref Person person)
{
if (clientID != 1)
return;
person = selectedPerson != null ? selectedPerson : new Person();
}
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:
static void Main(string[] args)
{
long clientId = 1;
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":
return;
case "N":
try
{
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)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine();
break;
default:
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:
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:
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)
{
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.