Dynamic events in a UserControl - c#

I've created a custom UserControl using the GUI, and I can't get it to accept dynamically added events from within a custom class.
Sorry if I get the exact code wrong, going from memory, but you get the gist.
C# .NET 2008, Winform
I have a "container class" that stores all my information.
The main WinForm has an array of these, and they each have a summary panel.
The main form also has a "work zone" that will let you access the container class.
The idea is, interacting with the CustomUserControl will cause stuff to happen within the ContainerClass. The control uses info from there, and I want it to update from within there.
ContainerClass
{
CustomUserControl tempControl;
public ContainerClass()
{
//do stuff
tempControl = new CustomUserControl([send information]);
tempControl.Click += new Event(localClickEvent);
}
public void localClickEvent(object sender, Event e)
{
//do stuff
}
}
.
public class Form1
{
public Form1()
{
//create several container objects
//for each container object, get it's SummaryPanel
//and add it to the FlowLayoutPanel
CustomUserControl tempControl = ContainerObject.GetCustomControl();
flp_summaryPanel.Controls.Add(tempControl);
}
}
When I execute this code, the dynamic event never fires.
I've done this sort of thing before, but it was always with custom classes that inherited the control I needed. I've never inherited UserControl, so I suspect I left something out.
I susepct this is an protection issue, but the compiler isn't catching it.
MORE INFO
This somehow got labeled as an ASP.net question. It's not, I'm using Winforms, though I never specifically said so.
One other thing that may be important: The dynamic event isn't in another control. It's in a custom class that functions as a large container object.
.
.
As always, thanks so much for your help! :)

Try attaching event handler after adding control to Controls collection
public ParentControl()
{
CustomUserControl tempControl = new CustomUserControl();
this.Controls.Add(tempControl);
tempControl.Click += new Event(localClickEvent);
}
It may be related to the fact that unless the control is added to NamingContainer the ClientID cannot be generated properly.

Which version of C# are you using? Try this:
public void ParentControl_Init()
{
CustomUserControl tempControl = new CustomUserControl();
this.Controls.Add(tempControl);
tempControl.Click += localClickEvent;
}
public void localClickEvent(object sender, EventArgs e)
{
//do stuff
}
You don't want to add controls and attach events in the constructor. Look at how ASP.NET does it and you'll see why.

Related

C# and WPF: controls inaccessible from another class

I just started fiddling around with C# and WPF.
Let's say that I want to instantiate a Black grid and that I want to randomly move around a red square in said Grid.
Currently I can basically do whatever I want as long as I keep everything in "MainWindow.xaml.cs"...
Now, my problem is that if I create a new class (e.g., "MakeStuffHappen.cs") and from it I try to access the Grid (named "MyGrid") that will be instantiated by MainWindow's constructor, intellisense doesn't "see" it.
I tried making a getter that returns "MyGrid" but then the compiler says that "an object reference is required for the non-static field, method, or property 'ProjectName.MainWindow.getGrid()'.
Obviously I cannot define MainWindow as a static class...
Any tips on how to solve this?
Thanks!
P.S. Since I'm evidently no programmer I'm not necessarily aware of the technical terms to use when looking up information... so I apologize in advance if this question has been already asked.
P.P.S. I saw this: Access MainWIndow Control from a class in a separate file but it doesn't help.
Once your view is initialized (when the OnInitialized event fires) you can pass the initialized Grid into your helper class:
MainWindow.xaml.cs:
public partial class MainWindow
{
MakeStuffHappen helper = null;
public MainWindow()
{
OnInitialized += (s,e)=> { helper = new MakeStuffHappen(this.MyGridName); }
}
}
MakeStuffHappen.cs
public class MakeStuffHappen
{
Grid theGrid = null;
public MakeStuffHappen(Grid grid)
{
theGrid = grid;
// Do stuff with the grid.
}
}

Fill combobox when calling from another class

I'm very new to C# and I have a problem filling a combobox when calling the method from another class. My source is like this
class 1
private void btn_login_Click(object sender, EventArgs e)
{
UserControl1 uc1 = new UserControl1();
uc1.fill_cbb();
}
class 2
public void fill_cbb()
{
cbb_table.Items.Add("Text1");
cbb_table.Items.Add("Text2");
cbb_table.SelectedIndex = 0;
}
When I do it that way the combobox is empty.
if it is Asp please take care to the event IsPostBack
Your problem is not calling the method from another class. I suppose UserControl1 is your custom user control, and that the "class 2" you mentioned is a userControl1.
The code would work as it is, but you are calling it on the wrong instance of that control.
In your btn_login_Click method, you are generating a totally new instance of UserControl1. You are of course allowed to do that, which is why Visual Studio would never mark it as an error, but uc1 will not be the control that actually sits in your form.
Let's say in your form you have named the control "cbxOptions". Then in the button click event, you have to write
cbxOptions.fill_cbb();
instead, if that combobox is also of type UserControl1. Then it should work just fine.
Warning, car analogy: It's like when you want a new paint job on your car. Then you buy a new car of the same model and take that to the paint shop, get it painted, then bring it to the junkyard and get it crushed. Your old car will still have the same old color of course.

Access method of an usercontrol from another usercontrol

On my form I have 2 UserControls (ButtonDiscount, ButtonAdvertisment) that inherit FadeControl. FadeControl inherits UserControl class and is used to do custom stuff on the controls like fade out.
My 2 UserControls each have just one button hence those two unimaginative names.
On click of that button on one of usercontrols, I need to access the method in FadeControl from the other UserControl. The other button does the opposite.
I've done the following, on the event click in the UserControl ButtonDiscount:
private void button1_Click(object sender, EventArgs e)
{
ButtonAdvertisment ba = (ButtonAdvertisment)this.Parent.Controls.Find("buttonAdvertisment1", true)[0];
ba.FadeOut(true);
}
It works like a charm, but I don't think this is the right way, is there another way to access the method from the parent class of the other UserControl?
I can't pass it thru a UserControl constructor, the designer breaks down every time.
You have 2 separate user controls which are not aware of each other, which is good and keeps your code loosely-coupled. Now what you are trying to do is to make them somehow know about each other and communicate. Making them aware of each other breaks the loose-coupling and is a bad design.
What I would suggest is creating a 3rd control which will hold the two together and will handle all the communication between them. Each of your original controls will have public events, to which the parent control can subscribe and handle appropriately.
Check mediator pattern for more info: http://en.wikipedia.org/wiki/Mediator_pattern
What you've done is fine - you could do it by exposing events that fired when you click the button in those controls, and then passing references to each other (subscribing to those, writing the code to fade 'this' control).
That might be a bit too much work for a simple solution, however.
What I would say about your solution is that if you were to change the name of control(s) then it stops working. You could instead do:
var ba = this.Parent.Controls.OfType<ButtonAdvertisement>().FirstOrDefault();
That way you're no longer tied to the control name - but the type of the control. You'll need a using System.Linq; in your code file for this to work. Of course, this relies on the fact that there is only ever one other instance of that control type in the parent.
If you're interested in the first solution I mentioned - then this code snippet should help demonstrate:
public class FadeControl {
public event EventHandler Clicked;
public void FadeOut(bool b){
}
public void AttachTo(FadeControl other){
//splitting this operation to a public and private allows us to
//initiate an attach publicly, but then recurse privately without
//causing a stack overflow
AttachToInternal(other);
other.AttachToInternal(this);
}
private void AttachToInternal(FadeControl other){
other.Clicked += Attached_Clicked;
}
protected virtual void Attached_Clicked(object sender, EventArgs e)
{
//fade me out
FadeOut(true);
}
// provides a way for the deriving class to raise the Clicked event
protected void OnButtonClicked(){
if(Clicked != null) Clicked(this, null);
}
}
public class ButtonDiscount : FadeControl {
Button _button;
//omitted: designer code
//this is your handler for the _button clicked event
private void _button_Clicked(object sender, EventArgs e){
//call the base class' OnButtonClicked method - to raise the event
OnButtonClicked();
//TODO: do work.
}
}
//omitted - code for the ButtonAdvertisement class
Once you have that done - in your form, assuming you have _buttonAdvertisement and _buttonDiscount members in your form and after they're initialised - you simply do:
_buttonAdvertisement.AttachTo(_buttonDiscount);
And that will immediately bind both controls to each other.
Note - in response to a comment below - I've made the event handler in FadeControl for another FadeControl's Clicked event protected and virtual - so you can override it.

Event handler firing for all controls instead of individually

I am having a rather odd problem with the Gecko Webbrowser control, I have created my own class which inherits off of the Gecko Webcontrol and within the constructor of this I have set an event:
class fooGeckoClass: Gecko.GeckoWebBrowser
{
public fooGeckoClass()
{
this.DomClick += new EventHandler<Gecko.GeckoDomEventArgs>(fooEventFunction);
}
private static void fooEventFunction(Object sender, Gecko.GeckoDomEventArgs e)
{
((Gecko.GeckoWebBrowser)sender).Navigate("www.foo.com");
}
}
I am using three of these controls in a manually created UserControl, the controls are loaded in dynamically at start up from a config file and added the the UserControl controls collection. When clicking on any of the three controls, all three will navigate to "www.foo.com" away from there original site. I had a look at:
e.StopPropagation();
Which specifies that it stops further propagation of events during an event flow, however it does also specify that it will handle all events in the current flow, I believe the events must have already been given to the controls before this has a chance to stop it as the the three controls will still fire the event. I also tried e.Handled = true to no avail. Has anyone encountered this problem before and have any kind of solution to make it only fire on the control that was clicked on?
EDIT:
It may be worth showing how the controls are added to the form seeing as this must be where the problem is occurring (it does not happen if the controls are just placed in a user control in a small test app).
private void fooUserControl_Load(object sender, EventArgs e)
{
if (!this.DesignMode)
{
for (int iControls = 0; iControls < geckObs.Count(); iControls ++)
{
fooGeckoClass geckControl = new fooGeckoClass();
this.Controls.Add(geckControl );
break;
}
}
}
Odd answer but I seem to have resolved the issue, DomClick was being called at first run, changing to DomMouseClick or DomMouseUp has completely resolved the issue. I assume DomClick must be an event unto itself as it also doesn't use the GeckoDomMouseEventArgs but the regular GeckoEventArgs.
EDIT:
To add to this, the site I was going to was actually calling DomClick when it had finished loading hence the reason it was being called at start up across all three browsers.

manually firing the event in c#

i want to fire an event manually using c#. For instance, say if i want to fire a Form_closing event of Form A from Form B. How to do it ??
After getting some comments. I think i need to explain more on this.
Since my Form A is reference to a .dll which creates a custom taskbar in the desktop,
There is a situation for me to close the that custom taskbar from Form B.
i already tried FormA.Close() from Form B. when i do this, the .dll is unloaded from the app domain and due to this the space occupied by the custom task bar is blocked.
But that is not the case when i click the close button in the custom task bar. when i do this the space is freed up.
This is the reason i want to fire the close event of Form A manually from Form B which will solve my issue.
Thanks.
We did the following in one project:
There was a GlobalNotifier class, which defined events we wanted to use in different modules of the application, like this
public static class GlobalNotifier
{
public static event VoidEventHandler EnvironmentChanged;
public static void OnEnvironmentChanged()
{
if (EnvironmentChanged != null)
{
EnvironmentChanged();
}
}
}
Then, you could raise this event anywhere when you needed to let the rest of the application know that the environment has changed, like this
GlobalNotifier.OnEnvironmentChanged();
And also you could subscribe to this event wherever you wanted to be notified about the fact that the environment has changed.
public ReportingService()
{
GlobalNotifier.EnvironmentChanged += new VoidEventHandler(GlobalNotifier_EnvironmentChanged);
}
void GlobalNotifier_EnvironmentChanged()
{
//reset settings
_reportSettings = null;
}
So, whenever you changed the environment, you raised the event, and everyone who needed to know about that and perform some actions, was notified.
Might be similar to what you need to achieve.
Also, if you need to pass parameters, you can define the event any way you like, basically -
public static event VoidEventHandler<SomeObject, List<OtherObject>> SomethingUpdated;
public static void OnSomethingUpdated(SomeObject sender, List<OtherObject> associations)
{
if (SomethingUpdated != null)
{
SomethingUpdated(sender, associations);
}
}
// ...
MyClass.SomethingUpdated+= new VoidEventHandler<SomeObject, List<OtherObject>>(MyClass_SomethingUpdated);
// ...
void MyClass_SomethingUpdated(SomeObject param1, List<OtherObject> param2)
{
//do something
}
You would call the OnFormClosing() method of the Form class. You can do this with any event that has a paired On...() method.
It sounds to me, from your comments, like you don't want to raise one form's event from the other; you just want to handle one form's event from the other.
Yes, you can do that. FormB needs to have a reference to FormA. There are several ways you can do this; one easy way is to have a FormA type property in your FormB class, like this:
public FormA TheOtherForm { get; set; }
You set that property to your instance of FormA, and then you add the event handler as you've described in the comments.
You don't have to use FormA as the type of your property; any form has the FormClosing event, so you can just use Form as the type if you want.
Close the form. That will raise the event. If you want to raise the event separately from closing the form, you're doing something wrong; move that code to a separate method that you can call from the event and from FormB.
FormAInstance.Close()
Starting with Visual Studio 2005 (.Net 2.0), forms have automatic default instances. It sounds like that's what you're using. You should know that this feature exists mainly for backwards compatibility with VB6. You're really better off creating and using a new instance of the form. When you do that, you should just be able to call the .Close() method on that instance without it's app domain closing.
If you want to re-use this space, you might also try simply .Hide()-ing the form rather than closing it.

Categories