How to use events and delegate to send data between forms? - c#

I need to create a Windows Form application that able to send data and to receive data from another Form instance. what mean, the Form is a publisher and a subscriber, both of them.
Unfortunately, I was sick that day, and I couldn't be in the lecture that day.
I'll explain again:
I have a small chat Form,
who have: new Instance button, received messages, and send message.
as you can see right below:
When I send a message, it will be shown in the "Recieved" textbox of ALL INSTANCES.
I guess that I need to use delegates and events.
How to do so? thanks!!

Here's a quick solution.. Let me know if you have any questions or if you find the comments confusing..
Here's the Form class (code behind). Notice that once the form is instantiated, it "subscribes" to an event by "wiring" an event handler to the HandleMessage event inside the Message class. Within the Form class, this is where the ListView's item collection is populated.
Whenever the New Form button is clicked, the same Form get's created and displayed (this allows for code re-use, since the same logic will be exactly the same for all instances of the Form)
public partial class Form1 : Form
{
Messages _messages = Messages.Instance; // Singleton reference to the Messages class. This contains the shared event
// and messages list.
/// <summary>
/// Initializes a new instance of the <see cref="Form1"/> class.
/// </summary>
public Form1()
{
InitializeComponent();
// Subscribe to the message event. This will allow the form to be notified whenever there's a new message.
//
_messages.HandleMessage += new EventHandler(OnHandleMessage);
// If there any existing messages that other forms have sent, populate list with them.
//
foreach (var messages in _messages.CurrentMessages)
{
listView1.Items.Add(messages);
}
}
/// <summary>
/// Handles the Click event of the buttonNewForm control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private void buttonNewForm_Click(object sender, EventArgs e)
{
// Create a new form to display..
//
var newForm = new Form1();
newForm.Show();
}
/// <summary>
/// Handles the Click event of the buttonSend control. Adds a new message to the "central" message list.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private void buttonSend_Click(object sender, EventArgs e)
{
string message = String.Format("{0} -- {1}", DateTime.UtcNow.ToLongTimeString(), textBox1.Text);
textBox1.Clear();
_messages.AddMessage(message);
}
/// <summary>
/// Called when [handle message].
/// This is called whenever a new message has been added to the "central" list.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
public void OnHandleMessage(object sender, EventArgs args)
{
var messageEvent = args as MessageEventArgs;
if (messageEvent != null)
{
string message = messageEvent.Message;
listView1.Items.Add(message);
}
}
}
Here's a Messages class that I created to handle a "central" list of messages that are sent between Forms. The Messages class is a singleton (meaning, it can be instantiated only once), which allow one list to be persisted throughout all instances of a Form. All Forms will share the same list, and get notified whenever a list has been updated. As you can see, the "HandleMessage" event is the Event that each Form will subscribe to whenever it has been created/shown.
If you take a look at the NotifyNewMessage function, this is called by the Messages class to notify the subscribes that there was a change. Since EventArgs are used to pass data to the subscribers, I've created a special EventArgs to pass the newly added messages to all subscribers.
class Messages
{
private List<string> _messages = new List<string>();
private static readonly Messages _instance = new Messages();
public event EventHandler HandleMessage;
/// <summary>
/// Prevents a default instance of the <see cref="Messages"/> class from being created.
/// </summary>
private Messages()
{
}
/// <summary>
/// Gets the instance of the class.
/// </summary>
public static Messages Instance
{
get
{
return _instance;
}
}
/// <summary>
/// Gets the current messages list.
/// </summary>
public List<string> CurrentMessages
{
get
{
return _messages;
}
}
/// <summary>
/// Notifies any of the subscribers that a new message has been received.
/// </summary>
/// <param name="message">The message.</param>
public void NotifyNewMessage(string message)
{
EventHandler handler = HandleMessage;
if (handler != null)
{
// This will call the any form that is currently "wired" to the event, notifying them
// of the new message.
handler(this, new MessageEventArgs(message));
}
}
/// <summary>
/// Adds a new messages to the "central" list
/// </summary>
/// <param name="message">The message.</param>
public void AddMessage(string message)
{
_messages.Add(message);
NotifyNewMessage(message);
}
}
/// <summary>
/// Special Event Args used to pass the message data to the subscribers.
/// </summary>
class MessageEventArgs : EventArgs
{
private string _message = string.Empty;
public MessageEventArgs(string message)
{
_message = message;
}
public String Message
{
get
{
return _message;
}
}
}
Also.. in order for the messages to be "displayed" correctly, don't forget the set the "View" property of the ListView control to "List". An easier (and preferred way) would simply be using the ObservableCollection list type, which already provides an event you can subscribe to.
Hope this helps.

Related

WCF Named Pipes across the user session on same machine. Possible?

Requirement
I am trying to create a "Single Instance" winforms app with args to be passed if second instance invoked. I hope Mutex will not work like this. I have made a WCF pipe on the winform app so that if another instance came, it will be notified to Main Instance with the required args which I will process the args on the Main Instance as required and second instance closed after notifying.
Issue:
The above code seems to be working fine, problem arises when it comes to multiple user session, When I logon to the machine as "User A" and opens my app, it opens up the app (Main instance). At the same time, if another user logon say "User B" and tries to open up the app, IT HAPPILY OPENS UP :(
Code:
Here is my code for single instance with WCF.
public static class SingleInstanceManager
{
/// <summary>
/// Raised when another instance attempts to start up.
/// </summary>
public static event StartupEventHandler OtherInstanceStarted;
/// <summary>
/// Checks to see if this instance is the first instance running on this machine. If it is not, this method will
/// send the main instance this instance's startup information.
/// </summary>
/// <param name="guid">The application's unique identifier.</param>
/// <returns>True if this instance is the main instance.</returns>
public static bool VerifySingleInstance(Guid guid)
{
if (!AttemptPublishService(guid))
{
NotifyMainInstance(guid);
return false;
}
return true;
}
/// <summary>
/// Attempts to publish the service.
/// </summary>
/// <param name="guid">The application's unique identifier.</param>
/// <returns>True if the service was published successfully.</returns>
private static bool AttemptPublishService(Guid guid)
{
try
{
ServiceHost serviceHost = new ServiceHost(typeof(SingleInstance));
NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
serviceHost.AddServiceEndpoint(typeof(ISingleInstance), binding, CreateAddress(guid));
serviceHost.Open();
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Notifies the main instance that this instance is attempting to start up.
/// </summary>
/// <param name="guid">The application's unique identifier.</param>
private static void NotifyMainInstance(Guid guid)
{
NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
EndpointAddress remoteAddress = new EndpointAddress(CreateAddress(guid));
using (ChannelFactory<ISingleInstance> factory = new ChannelFactory<ISingleInstance>(binding, remoteAddress))
{
ISingleInstance singleInstance = factory.CreateChannel();
singleInstance.NotifyMainInstance(Environment.GetCommandLineArgs());
}
}
/// <summary>
/// Creates an address to publish/contact the service at based on a globally unique identifier.
/// </summary>
/// <param name="guid">The identifier for the application.</param>
/// <returns>The address to publish/contact the service.</returns>
private static string CreateAddress(Guid guid)
{
return string.Format(CultureInfo.CurrentCulture, "net.pipe://localhost/{0}", guid);
}
/// <summary>
/// The interface that describes the single instance service.
/// </summary>
[ServiceContract]
private interface ISingleInstance
{
/// <summary>
/// Notifies the main instance that another instance of the application attempted to start.
/// </summary>
/// <param name="args">The other instance's command-line arguments.</param>
[OperationContract]
void NotifyMainInstance(string[] args);
}
/// <summary>
/// The implementation of the single instance service interface.
/// </summary>
private class SingleInstance : ISingleInstance
{
/// <summary>
/// Notifies the main instance that another instance of the application attempted to start.
/// </summary>
/// <param name="args">The other instance's command-line arguments.</param>
public void NotifyMainInstance(string[] args)
{
if (OtherInstanceStarted != null)
{
Type type = typeof(StartupEventArgs);
ConstructorInfo constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
StartupEventArgs e = (StartupEventArgs)constructor.Invoke(null);
FieldInfo argsField = type.GetField("_args", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(argsField != null);
argsField.SetValue(e, args);
OtherInstanceStarted(null, e);
}
}
}
}
On the Winforms Main:
static void Main()
{
//bool isSingle;
//var mut = new Mutex(true, "MarketFeeder", out isSingle);
var applicationId = new Guid("08f21b4e-86d7-4ddf-abcb-9a72cd2bbd4f");
if (SingleInstanceManager.VerifySingleInstance(applicationId))
{
SingleInstanceManager.OtherInstanceStarted += OnOtherInstanceStarted;
// Start the application
System.Windows.Forms.Application.EnableVisualStyles();
System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
main = new MainForm();
System.Windows.Forms.Application.Run(main);
}
}
Help needed on
Is there a way to communicate via Namedpipe across the users on same machine?, I don't want to go for "Windows service" for my requirement which I feel over kill for my requirement.
Already went though following links but no luck. Also they are too old I feel we have better solution for this in recent times. I hope there are some devs like me who are still working on Windows applications :P
Isolated Named Pipes in Terminal Server Sessions

How to override GridView OnItemDataBound method?

I want to create costume Gridview. I found very nice open source one but the problem is that it uses DataGrid and when I change the inherited class to GridView, I get the following error
AADGridView.OnItemDataBound(DataGridItemEventArgs
e):not suitable method found to override
I have override here :
//public class AADGridView : DataGrid, IPostBackEventHandler
public class AADGridView : GridView, IPostBackEventHandler
{
/// <summary>
/// Gets or sets a value that indicates whether the auto filter is displayed in the AADGrid.AADGridControl.
/// </summary>
[Bindable(true), Category("Appearance"), Description("Whether to show the control's auto filter."), DefaultValue(true),]
/// <summary>
/// Override the DataGrid constructor.
/// </summary>
public AADGridView() : base()
{
// create the ArrayList to contain the DropDownList controls and the SortedList objects added to the header items;
//list = new ArrayList();
sort = new ArrayList();
filter = true;
}
/// <summary>
/// Override the OnItemDataBound event.
/// </summary>
/// <param name="e"></param>
override protected void OnItemDataBound(DataGridItemEventArgs e)
{
//Some Code
base.OnItemDataBound(e);
}
as you see in the code, I just change DataGrid to GridView. Both of them has OnItemDataBound,So whats the problem?
Thanks
The event arguments are of type DataGridItemEventArgs. You have to change the parameter type to the new base class' parameter.

C# LabelLink LinkArea detection for mouse position

Is there a way to determine if the mouse is within the LinkArea of a LinkLabel control in C#?
Any help would be greatly appreciated.
You can use Mouse Enter Event
linkLabel1.MouseEnter += new EventHandler(linkLabel1_MouseEnter);
private void linkLabel1_MouseEnter(object sender, EventArgs e)
{
MessageBox.Show("Mouse is within link area");
}
This cannot be done. The MouseEnter event suggested in the other answer at the time of this writing will fire any time the mouse enters the control area, regardless of whether it is actually over link text or not.
If your LinkLabel control's boundaries are small relative to its content, then the MouseEnter event may work well enough. But in the case where (for example) you want your link to change color when the mouse hovers over the link text and there is a lot of area within the control around the text, then this approach will not work.
On a somewhat unrelated note, this also prevents you from detecting which link within the LinkLabel is currently being hovered over if you have multiple, as mentioned here.
One more detail in case anybody is wondering: the LinkLabel.LinkArea property is not what you are looking for, either. That only determines which characters within the LinkLabel are actually part of a link, and not the actual area they occupy onscreen.
To wrap up, the only way you may be able to get the functionality you are looking for is to implement your own custom control which behaves similarly to the Label or LinkLabel control, but adds the methods and/or properties that you need.
Since the original LinkLabel has a protected function PointInLink this is not hard to do:
using System;
using System.Windows.Forms;
namespace MyControlNameSpace
{
/// <summary>
/// Data for a HoveredLinkChanged-Handler.
/// </summary>
public class HoveredLinkChangedEventArgs : EventArgs
{
private readonly LinkLabel.Link m_Link;
/// <summary>
/// Creates data for a HoveredLinkChanged-Handler
/// </summary>
/// <param name="link">the Link, with the mouse pointer over it</param>
public HoveredLinkChangedEventArgs(LinkLabel.Link link)
{
m_Link = link;
}
/// <summary>
/// Returns the hovered Link
/// </summary>
public LinkLabel.Link HoveredLink
{
get { return m_Link; }
}
}
/// <summary>
/// The structure of a HoveredLinkChanged-Handler
/// </summary>
public delegate void HoveredLinkChangedEventHandler(
object sender, HoveredLinkChangedEventArgs e);
/// <summary>
/// Adds to LinkLabel the possiblity to react on changes
/// of the hovered Link (e.g. to alter a TooltipText).
/// </summary>
public class LinkLabelEx : LinkLabel
{
private Link m_HoveredLink;
/// <summary>
/// Occurs, when another Link is hovered.
/// </summary>
public event HoveredLinkChangedEventHandler HoveredLinkChanged;
/// <summary>
/// Raises the HoveredLinkChanged event
/// </summary>
/// <param name="hoveredLink">the hovered Link</param>
protected virtual void OnHoveredLinkChanged(Link hoveredLink)
{
if (HoveredLinkChanged != null)
HoveredLinkChanged(this,
new HoveredLinkChangedEventArgs(hoveredLink));
}
/// <summary>
/// Raises the Control.OnMouseMove(MouseEventArgs) event.
/// </summary>
/// <param name="e">a MouseEventArgs containing the event data</param>
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
Link currentLink = PointInLink(e.X, e.Y);
if (Equals(currentLink, m_HoveredLink)) return;
m_HoveredLink = currentLink;
OnHoveredLinkChanged(m_HoveredLink);
}
}
}
Then there are only two things left to do:
Add an eventhandler to your LinkLabelEx, e.g.:
linkLabelEx1.HoveredLinkChanged += linkLabelEx1_HoveredLinkChanged;
Set the tooltiptext according to the link given in the eventArgs, e.g.:
void linkLabelEx1_HoveredLinkChanged(object sender, HoveredLinkChangedEventArgs e)
{
string ttt = e.Link == null
? string.Empty
: e.Link.Description;
toolTip1.SetToolTip((Control)sender, ttt);
}

XML comments on delegate declared events

I am visiting some old code, and there are quite a few events declared with delegates manually rather than using EventHandler<T>, like this:
/// <summary>
/// Delegate for event Added
/// </summary>
/// <param name="index">Index of the item</param>
/// <param name="item">The item itself</param>
public delegate void ItemAdded(int index, T item);
/// <summary>
/// Added is raised whenever an item is added to the collection
/// </summary>
public event ItemAdded Added;
All well and good, until I come to use sandcastle to document the library, because it then can't find any XML comments for the private Added field that is generated by the event declaration. I want to try and sort that out, but what I would like to do is either:
Get sandcastle to ignore the auto-generated private field without telling it to ignore all private fields entirely
or
Get XML comments generated for the private field
Is there any way of achieving this without re-factoring the code to look like this:
/// <summary>
/// Delegate for event <see cref="Added"/>
/// </summary>
/// <param name="index">Index of the item</param>
/// <param name="item">The item itself</param>
public delegate void ItemAdded(int index, T item);
/// <summary>
/// Private storage for the event firing delegate for the <see cref="Added"/> event
/// </summary>
private ItemAdded _added;
/// <summary>
/// Added is raised whenever an item is added to the collection
/// </summary>
public event ItemAdded Added
{
add
{
_added += value;
}
remove
{
_added -= value;
}
}
Although sub-optimal, I found a way around this, which was to manually put the XML comments into the file that contains namespace-level documentation for the project, along these lines:
<member name="F:myCompany.Common.Collections.Generic.EventableCollection`1.Added">
<summary>
Auto-generated backing field for the <see cref="E:myCompany.Common.Collections.Generic.EventableSortedList`1.Added">Added</see> event
</summary>
</member>
This then gives roughly what I needed.

Trace PRISM / CAL events (best practice?)

Ok,
this question is for people with either a deep knowledge of PRISM or some magic skills I just lack (yet). The Background is simple: Prism allows the declaration of events to which the user can subscribe or publish. In code this looks like this:
_eventAggregator.GetEvent<LayoutChangedEvent>().Subscribe(UpdateUi, true);
_eventAggregator.GetEvent<LayoutChangedEvent>().Publish("Some argument");
Now this is nice, especially because these events are strongly typed, and the declaration is a piece of cake:
public class LayoutChangedEvent : CompositePresentationEvent<string>
{
}
But now comes the hard part: I want to trace events in some way. I had the idea to subscribe using a lambda expression calling a simple log message. Worked perfectly in WPF, but in Silverlight there is some method access error (took me some time to figure out the reason).. If you want to see for yourself, try this in Silverlight:
eA.GetEvent<VideoStartedEvent>().Subscribe(obj => TraceEvent(obj, "vSe", log));
If this would be possible, I would be happy, because I could easily trace all events using a single line to subscribe. But it does not... The alternative approach is writing a different functions for each event, and assign this function to the events. Why different functions? Well, I need to know WHICH event was published. If I use the same function for two different events I only get the payload as argument. I have now way to figure out which event caused the tracing message.
I tried:
using Reflection to get the causing event (not working)
using a constructor in the event to enable each event to trace itself (not allowed)
Any other ideas?
Chris
PS: Writing this text took me most likely longer than writing 20 functions for my 20 events, but I refuse to give up :-) I just had the idea to use postsharp, that would most likely work (although I am not sure, perhaps I end up having only information about the base class).. Tricky and so unimportant topic...
Probably the easiest thing would be to subclass CompositePresentationEvent and override the behavior of the Publish event. Here's the source for CompositePresentationEvent:
http://compositewpf.codeplex.com/SourceControl/changeset/view/26112#496659
Here's the current Publish behavior:
public virtual void Publish(TPayload payload)
{
base.InternalPublish(payload);
}
So you could just add a little to this:
public virtual override void Publish(TPayload payload)
{
ILoggerFacade logger = ServiceLocator.Current.GetInstance<ILoggerFacade>();
logger.Log("Publishing " + payload.ToString(), Category.Debug, Priority.Low);
base.InternalPublish(payload);
}
Here I'm using the logger facility built into Prism, but feel free to substitute your own (or better, just implement ILoggerFacade!).
I was surprised that there were any default messages being published or places to plug in tracing in this system... as much as EventAggregator is abused by people, you'd think this would be a big request!
A little late but better late than never! I recently had the same problem and this is how I solved it.
First, I didn't like the Prism method of publishing/subscribing to events, so I used a method like this instead:
http://neverindoubtnet.blogspot.com/2009/07/simplify-prism-event-aggregator.html
This post above suggests using Extension methods on Event Aggregator to simplify the call to publish/subscribe. As a result your client code looks like this:
IEventAggregator ev;
ev.Publish<MyCustomMessage>();
//or
ev.Publish(new MyCustomMessage(someData));
//and similarly subscription
ev.Subscribe<MyCustomMessage(this.OnCustomMessageReceived);
// ...
private void OnCustomMessageReceived(MyCustomMessage message)
{
// ...
}
// With a BaseMessageEvent class as follows (see the blog post above for where this comes from)
/// <summary>
/// Base class for all messages (events)
/// </summary>
/// <typeparam name="TMessage">The message type (payload delivered to subscribers)</typeparam>
public class BaseEventMessage<TMessage> : CompositePresentationEvent<TMessage>
{
}
Ok this is great, but rather than hacky extension methods I implemented my own event service as follows:
/// <summary>
/// The EventService instance
/// </summary>
public class EventService : IEventService
{
private readonly IEventAggregator eventAggregator;
private readonly ILoggerFacade logger;
/// <summary>
/// Initializes a new instance of the <see cref="EventService"/> class.
/// </summary>
/// <param name="logger">The logger instance.</param>
/// <param name="eventAggregator">The event aggregator instance.</param>
public EventService(IEventAggregator eventAggregator, ILoggerFacade logger)
{
this.logger = logger;
this.eventAggregator = eventAggregator;
}
#region IEventService Members
/// <summary>
/// Publishes the event of type TMessageType to all subscribers
/// </summary>
/// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
public void Publish<TMessageType>() where TMessageType : BaseEventMessage<TMessageType>, new()
{
TMessageType message = Activator.CreateInstance<TMessageType>();
this.Publish(message);
}
/// <summary>
/// Publishes the event of type TMessageType to all subscribers
/// </summary>
/// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
/// <param name="message">The message to publish</param>
public void Publish<TMessageType>(TMessageType message) where TMessageType : BaseEventMessage<TMessageType>, new()
{
// Here we can log our message publications
if (this.logger != null)
{
// logger.log etc..
}
this.eventAggregator.GetEvent<TMessageType>().Publish(message);
}
/// <summary>
/// Subscribes to the event of type TMessage
/// </summary>
/// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
/// <param name="action">The action to execute when the event is raised</param>
public void Subscribe<TMessageType>(Action<TMessageType> action) where TMessageType : BaseEventMessage<TMessageType>, new()
{
// Here we can log our message publications
if (this.logger != null)
{
// logger.log etc..
}
this.eventAggregator.GetEvent<TMessageType>().Subscribe(action);
}
#endregion
}
Then I register IEventService/EventService as a singleton in the bootstrapper and forget about using the IEventAggregator, just use this (however if someone uses the IEventAggregator, its the same instance as that used by the EventService so will still work).
Finally, another trick to add is to use the Stack Frame to tell me where publications and subscriptions are coming from. Note this is a slow process (unwinding the stack frame) so use it sparingly. If you are
raising an event regularly then perhaps put a flag in your BaseEventMessage and check that to see whether to log publications for certain event types.
// Inside Publish method ... Log the subscription
if (this.logger != null)
{
Type messageType = typeof(TMessageType);
Type callingType = GetCallingType();
string methodName = GetCallingMethod().Name;
// Log the publication of this event
this.logger.Log(
string.Format("Event {0} was published by {1}.{2}()",
messageType.Name,
callingType.Name,
methodName),
Category.Debug,
Priority.Low));
}
// Additional methods to add to EventService to get the calling type/class
//
/// <summary>
/// Gets the Type that called the method or property where GetCallingType is called
/// </summary>
/// <returns>The class type that called</returns>
[MethodImplAttribute(MethodImplOptions.NoInlining)]
public static Type GetCallingType()
{
int skip = 2;
MethodBase method = new StackFrame(skip, false).GetMethod();
return method.DeclaringType;
}
/// <summary>
/// Gets the Method that called the method or property where GetCallingMethod is called
/// </summary>
/// <returns>The method type that was called</returns>
public static MethodBase GetCallingMethod()
{
return new StackFrame(2, false).GetMethod();
}
Note the above won't work in Silverlight (the use of the StackFrame), but the rest does. I've found this invaluable when debugging the multitude of events flying around a Prism app!

Categories