Getting update from UserControl - c#

Can someone tell me how can I have a feature in my UserControl, that can let the host windowsform know what is the control is doing?
For example my usercontrol has a filebrowser, and if user uses this file browser to open a file I want in the statusstrip bar of my form to write "Loading file(s)".
Will this require using events? if so, how can I have a single event inside usercontrol to report anything it does (then I guess I have to call this event in all methods in the usercontrol).

Simple
Yes, expose an event on the user control that the Form can subscribe to. You should use the standard event pattern:
class MyUserControl : UserControl
{
public event EventHandler<EventArgs> FileOpened;
protected virtual void OnFileOpened(EventArgs e)
{
EventHandler<EventArgs> handler = FileOpened;
if (handler != null)
handler(this, e);
}
}
Then when the file is opened you call OnFileOpened(EventArgs.Empty) which fires the event.
With custom EventArgs
Now the Form probably needs to know what file was opened. You could expose a property on the user control that the Form can use to find out, or you can provide that information in your event like so:
public class FileOpenedEventArgs : EventArgs
{
private string filename;
public FileOpenedEventArgs(string filename)
{
this.filename = filename;
}
public string Filename { get { return filename; } }
}
class MyUserControl : UserControl
{
public event EventHandler<FileOpenedEventArgs> FileOpened;
protected virtual void OnFileOpened(FileOpenedEventArgs e)
{
EventHandler<FileOpenedEventArgs> handler = FileOpened;
if (handler != null)
handler(this, e);
}
}
Then you fire the event with OnFileOpened(new FileOpenedEventArgs(filename)).
Optimal
When you create an event handler public event delegate Name;, you are allocating storage for the delegate on your object. Objects (especially Controls) often have a huge number of events that are never subscribed to. That's a whole lot of allocated storage not being used. There's an optimization built into the framework in the form of a EventHandlerList. This handy object stores event handlers only when they are actually used. All System.Windows.Forms.Control objects derive from System.ComponentModel.Component and it already provides an (protected) EventHandlerList that you can access in your derived Control.
To use it, you first create a static object that uniquely identifies your event, and then you provide the add {} and remove {} methods manually. Like so:
class MyUserControl : UserControl
{
private static readonly object FileOpenedKey = new Object();
public event EventHandler<FileOpenedEventArgs> FileOpened
{
add { Events.AddHandler(FileOpenedKey, value); }
remove { Events.RemoveHandler(FileOpenedKey, value); }
}
protected virtual void OnFileOpened(FileOpenedEventArgs e)
{
var handler = (EventHandler<FileOpenedEventArgs>)Events[FileOpenedKey];
if (handler != null)
handler(this, e);
}
}

Yes, you will need to create an event and subscribe to it. One suggestion following the standard pattern for events:
enum ControlStatus {Idle, LoadingFile, ...}
class StatusChangedEventArgs : EventArgs
{
public ControlStatus Status {get; private set;}
public StatusChangedEventArgs(ControlStatus status)
: base()
{
this.Status = status;
}
}
partial class MyControl : UserControl
{
public ControlStatus Status {get; private set;}
public event EventHandler<StatusChangedEventArgs> StatusChanged;
protected virtual void OnStatusChanged(StatusChangedEventArgs e)
{
var hand = StatusChanged;
if(hand != null) hand(this, e);
}
void LoadFiles()
{
...
Status = ControlStatus.LoadingFiles;
OnStatusChanged(new StatusChangedEventArgs(this.Status));
...
Status = ControlStatus.Idle;
OnStatusChanged(new StatusChangedEventArgs(this.Status));
}
}
partial class MyHostWindowsForm : Form
{
public MyHostWindowsForm()
{
var ctl = new MyControl();
...
ctl.StatusChanged += ctl_StatusChanged;
}
void ctl_StatusChanged(object sender, StatusChangedEventArgs e)
{
switch(e.Status)
{
case ControlStatus.Idle:
statusStripBar.Text = null;
break;
case ControlStatus.LoadingFiles:
statusStripBar.Text = "Loading file(s)";
break;
...
}
}
}

Related

The event can only appear in the left hand side of += or -+ [duplicate]

I have a base class that contains the following events:
public event EventHandler Loading;
public event EventHandler Finished;
In a class that inherits from this base class I try to raise the event:
this.Loading(this, new EventHandler()); // All we care about is which object is loading.
I receive the following error:
The event 'BaseClass.Loading' can only appear on the left hand side of += or -= (BaseClass')
I am assuming I cannot access these events the same as other inherited members?
What you have to do , is this:
In your base class (where you have declared the events), create protected methods which can be used to raise the events:
public class MyClass
{
public event EventHandler Loading;
public event EventHandler Finished;
protected virtual void OnLoading(EventArgs e)
{
EventHandler handler = Loading;
if( handler != null )
{
handler(this, e);
}
}
protected virtual void OnFinished(EventArgs e)
{
EventHandler handler = Finished;
if( handler != null )
{
handler(this, e);
}
}
}
(Note that you should probably change those methods, in order to check whether you have to Invoke the eventhandler or not).
Then, in classes that inherit from this base class, you can just call the OnFinished or OnLoading methods to raise the events:
public AnotherClass : MyClass
{
public void DoSomeStuff()
{
...
OnLoading(EventArgs.Empty);
...
OnFinished(EventArgs.Empty);
}
}
You can only access an event in the declaring class, as .NET creates private instance variables behind the scenes that actually hold the delegate. Doing this..
public event EventHandler MyPropertyChanged;
is actually doing this;
private EventHandler myPropertyChangedDelegate;
public event EventHandler MyPropertyChanged
{
add { myPropertyChangedDelegate += value; }
remove { myPropertyChangedDelegate -= value; }
}
and doing this...
MyPropertyChanged(this, EventArgs.Empty);
is actually this...
myPropertyChangedDelegate(this, EventArgs.Empty);
So you can (obviously) only access the private delegate instance variable from within the declaring class.
The convention is to provide something like this in the declaring class..
protected virtual void OnMyPropertyChanged(EventArgs e)
{
EventHandler invoker = MyPropertyChanged;
if(invoker != null) invoker(this, e);
}
You can then call OnMyPropertyChanged(EventArgs.Empty) from anywhere in that class or below the inheritance heirarchy to invoke the event.
I am assuming I cannot access these events the same as other inherited members?
Precisely. It's customary to provide a protected function OnXyz or RaiseXyz for each event in the base class to enable raising from inherited classes. For example:
public event EventHandler Loading;
protected virtual void OnLoading() {
EventHandler handler = Loading;
if (handler != null)
handler(this, EventArgs.Empty);
}
Called in the inherited class:
OnLoading();
You can try this way, It works for me:
public delegate void MyEventHaldler(object sender, EventArgs e);
public class B
{
public virtual event MyEventHaldler MyEvent;
protected override void OnChanged(EventArgs e)
{
if (MyEvent != null)
MyEvent(this, e);
}
}
public class D : B
{
public override event MyEventHaldler MyEvent;
protected override void OnChanged(EventArgs e)
{
if (MyEvent != null)
MyEvent(this, e);
}
}
not to resurrect an old thread but in case anybody is looking, what I did was
protected EventHandler myPropertyChangedDelegate;
public event EventHandler MyPropertyChanged
{
add { myPropertyChangedDelegate += value; }
remove { myPropertyChangedDelegate -= value; }
}
This lets you inherit the event in a derived class so you can invoke it without requiring to wrap the method while keeping the += syntax. I guess you could still do that with the wrapping methods if you did
public event EventHandler MyPropertyChanged
{
add { AddDelegate(value); }
remove { RemoveDelegate(value); }
}

:How to create a event in c# without delegates?

I tried like this but it will return null values,
public event EventHandler<EventArgs> myevent;
public void Btn_Click(object sender, EventArgs e)
{
if (myevent!= null) //Here I get null value.
myevent(null, new EventArgs());
}
How to achieve the event fire?
Edit:
I have a UserControl in that user control which contain button event,inside the ButtonClick method I created this event.
I have a Form. In that form i m using this UserControl. So i need to Fire a event from User Control button click event to Form page particular function.
I wrote up a very simple, and basic solution for you. Please read the comments in the code to make sense of the solution. If anything is unclear, please ask questions.
Below sample, will cause the event to fire, if the person's name changes:
Here is the Person object:
public class Person
{
//Event to register to, when you want to capture name changed of person
public event EventHandler<NameChangedEventArgs> nameChangedEvent;
//Property
private string _name;
public string Name
{
get { return _name; }
set
{
this._name = value;
//Call the event. This will trigger the OnNameChangedEvent_Handler
OnNameChangedEvent_Handler(new NameChangedEventArgs() {NewName = value});
}
}
private void OnNameChangedEvent_Handler(NameChangedEventArgs args)
{
//Check if event is null, if not, invoke it.
nameChangedEvent?.Invoke(this, args);
}
}
//Custom event arguments class. This is the argument type passed to your handler, that will contain the new values/changed values
public class NameChangedEventArgs : EventArgs
{
public string NewName { get; set; }
}
Here is the code that instantiates and uses the Person object:
class Program
{
static void Main(string[] args)
{
//Instantiate person object
var person = new Person();
//Give it a default name
person.Name = "Andrew";
//Register to the nameChangedEvent, and tell it what to do if the person's name changes
person.nameChangedEvent += (sender, nameChangedArgs) =>
{
Console.WriteLine(nameChangedArgs.NewName);
};
//This will trigger the nameChanged event.
person.Name = "NewAndrewName";
Console.ReadKey();
}
}
can you please try the below way of calling event :
public event EventHandler myevent;
myevent += new EventHandler(Button1_Click);
if (myevent != null) //Here I get null value.
myevent(1, new EventArgs());

Creating an event in a dll and handling the event in a Form

I have created a DLL using the following code. I have compiled this code as a DLL.
namespace DllEventTrigger
{
public class Trigger
{
public delegate void AlertEventHandler(Object sender, AlertEventArgs e);
public Trigger()
{
}
public void isRinging()
{
AlertEventArgs alertEventArgs = new AlertEventArgs();
alertEventArgs.uuiData = "Hello Damn World!!!";
CallAlert(new object(), alertEventArgs);
}
public event AlertEventHandler CallAlert;
}
public class AlertEventArgs : EventArgs
{
#region AlertEventArgs Properties
private string _uui = null;
#endregion
#region Get/Set Properties
public string uuiData
{
get { return _uui; }
set { _uui = value; }
}
#endregion
}
}
Now I'm trying to handle the event triggered by this dll in a forms application with this code.
namespace DLLTriggerReciever
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Trigger trigger = new Trigger();
trigger.isRinging();
trigger.CallAlert += new Trigger.AlertEventHandler(trigger_CallAlert);
}
void trigger_CallAlert(object sender, AlertEventArgs e)
{
label1.Text = e.uuiData;
}
}
}
My problem i'm not sure where i went wrong. Please suggest.
You need to assign your event handler before the event is actually fired, otherwise the code will throw a NullReferenceException.
trigger.CallAlert += new Trigger.AlertEventHandler(trigger_CallAlert);
trigger.isRinging();
Additionally, it's a recommended practice to check first, whether there are handlers assigned:
var handler = CallAlert; // local variable prevents a race condition to occur
if (handler != null)
{
handler(this, alertEventArgs);
}
as #Gene said, you need to register the event before raising it.
anyway, it's a good practice to check if someone is register to the event you're about to raise by checking for null.
like this:
if (this.CallAlert != null)
this.CallAlert(new object(), alertEventArgs);

How can I create a custom event?

I have a custom set of UserControls: NavigationBar and NavigationItem.
I'd like that whenever the user clicks anywhere in the NavigationItem, an event is fired. I don't know how to set this up.
http://i.stack.imgur.com/ocP2D.jpg
I've tried this:
public partial class NavigationBar : UserControl
{
public NavigationBar()
{
InitializeComponent();
SetupEvents();
}
public List<NavigationItem> NavigationItems { private get; set; }
public NavigationItem SelectedItem { get; set; }
private void SetupEvents()
{
navigationItem1.Click += new EventHandler(navigationItemClick);
}
void navigationItemClick(object sender, EventArgs e)
{
MessageBox.Show("Clicked on " + sender.ToString());
}
}
But that event only fires when the user specifically clicks on the NavigationItem control, but not when he clicks on the picture or text. (Those are PictureBox and Label).
What would be the best course of action? I'd like to create something well, not hacky code. Thanks!
Put something like this into your class:
public event EventHandler NavigationItemClick;
This creates a new event in your class named NavigationItemClick. The form designer will even see it.
In your method navigationItemClick you can do this to call the event.
EventHandler handler = this.NavigationItemClick;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
It is important to save the event into the handler variable to avoid race conditions. EventHandler is a delegate, so you call it like a method, hence the line in the if statement. The if itself makes sure that someone has attached to your event.

How to make own event handler?

I am making a windows forms project in C#, in which I made a class LabelX which inherits System.Windows.Forms.Label, then added a property Mass of float type
Now, my question is how can I handle, when value of Mass is changed.
e.g.:
When user enter value zero or less than zero
I want to fire a message that "Mass can't be zero or negative"
If I am interpreting this correctly, there are two parts to this. First, you need to detect invalid values and throw exceptions. Second, you need to raise an event when the property changes. This can be achieved as follows.
private float mass;
public float Mass
{
get
{
return this.mass;
}
set
{
if (value <= 0.0F)
{
throw new ArgumentOutOfRangeException("Mass cannot be zero or negative.");
}
if (this.mass != value)
{
this.mass = value;
OnMassChanged(EventArgs.Empty);
}
}
}
public event EventHandler MassChanged;
protected virtual void OnMassChanged(EventArgs args)
{
var handler = this.MassChanged;
if (handler != null)
{
handler(this, args);
}
}
To show a message if an invalid entry is made, you should put a try \ catch block around the call to set Mass and catch the ArgumentOutOfRangeException.
Try the following:
// Created an empty form with a LabelX control on it.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Added this event from the property manager.
private void labelX1_MassChanged(object sender, EventArgs e)
{
var label = (LabelX)sender;
if (label.Mass <= 0.0)
MessageBox.Show("Mass is less than or equal to 0");
}
}
public class LabelX : Label
{
private float _mass;
public float Mass
{
get { return _mass; }
set
{
if (!value.Equals(_mass))
{
_mass = value;
OnMassChanged(EventArgs.Empty);
}
}
}
public event EventHandler MassChanged;
protected virtual void OnMassChanged(EventArgs e)
{
if (MassChanged != null)
MassChanged(this, e);
}
}
Outside of your LabelX class, create the following class:
public class MassChangedEventArgs : EventArgs
{
public float Mass { get; private set; }
public MassChangedEventArgs(float mass)
{
this.Mass = mass;
}
}
Also outside of your LabelX class, create the following delegate. This will be your event handler.
public delegate void MassChangedEventHandler(object sender, MassChangedEventArgs e);
Within your LabelX class, create an event to broadcast:
public class LabelX
{
public event MassChangedEventHandler MassChanged;
//the rest of your code here...
}
You'll also want to create a private instance method that will fire your event.
public class LabelX
{
public event MassChangedEventHandler MassChanged;
private void OnMassChanged()
{
if(MassChanged!=null)
this.MassChanged(this, new MassChangedEventArgs(this.Mass));
}
//the rest of your code here...
}
Finally, whenever your Mass property changes, call OnMassChanged. For instance:
public class LabelX
{
private float mass;
public float Mass
{
get
{
return mass;
}
set
{
mass = value;
OnMassChanged();
}
}
public event MassChangedEventHandler MassChanged;
private void OnMassChanged()
{
if(MassChanged!=null)
this.MassChanged(this, new MassChangedEventArgs(this.Mass));
}
//the rest of your code here...
}
When you want to handle that event on a per-instance basis, you just have to register a listener with the MassChanged event of your underlying object and perform whatever actions are necessary.
Events are a common pattern used in the framework. The process typically involves defining a delegate to be used as the event handlers, declaring the event using the handler, defining methods to raise the event, then hooking up to the properties the logic to raise the event.
The message you describe is better done as an Exception but here's an example to define the MassChanged event.
// Define event args if you have additional
// information to pass to your event handlers
public class MassChangedEventArgs : EventArgs
{
public MassChangedEventArgs(int oldMass)
{
OldMass = oldMass;
}
public int OldMass { get; private set; }
}
public class SomeObject
{
// There's a generic event handler delegate that can be
// used so you only need to define the event arguments.
public event EventHandler<MassChangedEventArgs> MassChanged;
// Convenience method to raise the event
protected virtual void OnMassChanged(MassChangedEventArgs e)
{
if (MassChanged != null)
MassChanged(this, e);
}
public int Mass
{
get
{
return mass;
}
set
{
// Your checks here
if (value <= 0)
throw new ArgumentOutOfRangeException("Mass", "Mass can't be zero or negative");
// Should only raise the event if the new value is different
if (value != mass)
{
// Change the mass
MassChangedEventArgs e = new MassChangedEventArgs(mass);
mass = value;
// Raise the event
OnMassChanged(e);
}
}
}
private int mass;
}
After that, it's just a matter of registering handlers to the event and going from there.
I am quite sure you you would like to 'fire' an exception in your case.
This more of a validation logic issue such AOP code contracts concept.
But if you really like to create an event for it you have to at least:
1) create an event storage variable in your label class
public event EventHandler MassChanged;
2) in your property (note that you loose the ability to use code gen functions of c# 3 for
which 'auto' implement the field to store your Mass property value)
public bool Mass
{
get { return _mass; }
set {
// check if value is invalid (0 or less) && that event subscribers exist
if(value<=0 && MassChanged != null) { MassChanged(this, null); }
else // otherwise assign ...
{
_mass = value;
}
}
}
3) create an event handler of type EventHandler
Best to read the msdn article for events: link text
Again I am pretty sure you are not handling exceptions properly in the app
if you need an event for this. I mean there is nothing wrong but events are
usually not used as means of value validations.

Categories

Resources