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.
Related
My scenario is quite simple, in fact I thought it would cause no problem but it does, can anyone help?
My MainPage begins with this:
namespace PhoneApp
{
public partial class MainPage : PhoneApplicationPage { /* snip */ }
}
Later in the page is this simple method:
private void MakeSound(object sender, MouseButtonEventArgs e)
All right, everything is fine.
But on my SettingsSample.xaml I have a ListBoxItem that calls the method MakeSoundvia SelectionChanged property. VS studio tells me that 'event handler MakeSoundis not found on class PhoneApp.SettingsSample '
The code for this page begins with:
namespace PhoneApp
{
public partial class SettingsSample : PhoneApplicationPage { /* snip */ }
}
Copying the code of the method on SettingsPage.xaml.cs doesn't work for some reason, but I feel like it should possible to use the method described on MaingePage.xaml.cs, especially since their respective code resides in the same Namespace.
I tried to add
using PhoneApp;
on SettingsSample.xaml.cs. Not exceptionally clever, but I have no other idea
Your event function should read EventClickOnMyListBox and be located in the code-behind for your page. This function would then call your makeasound function (that you can put wherever you want). And it could be called by another event or function.
There is a semantics distinction between the event itself and what the event does. Your event is not makeasound, your event will cause a sound to be made, among possible other things.
If ever you want to add a popup to add a visual effect, you'll be stuck if you called directly the makeasound function, whereas you'll just have to add a line to your EventClickOnMyListBox in the other scenario.
That's why xaml works that way, and allows you to refer only to object in the enclosing class (or if there is a way I don't know it / and never needed it). You should minimize code-behind code (this is the way of thinking of the MVVM pattern if you want to go further down this road).
Replying to your comment, here is some code to get you started:
Your event should be in the code-behind of your SettingSample page, and should call the makeasound function instead of being the makeasound function:
private void MenuItem_Click(object sender, MouseButtonEventArgs e)
{
Whatever.Myclass.MakeASound (blabla); // So typically MakeASound would be static
}
And now same thing in your main page: you do not use your private void MakeSound(object sender, MouseButtonEventArgs e) as the event for a click, but an event handler just as the one above.
Take a step back and you'll see it's very natural: you want two things to do the same. But one thing cannot be the exact same thing as the first, for language-related reasons (read about C# and XAML to understand why).
So you put your feature in a third component, accessible from the first two. And you call this component from each of them. This way, they both share access to the same feature, and everybody is happy.
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.
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.
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.
I am trying to handle a mouseclick event on a particular form that should fire if the mouse cursor falls between a set of coordinates - lets say a square.
I understand that if I had an empty form I could simply tie in to the mousemove event and off I go. But in reality there may be up to 10 different overlapping controls and in my test app the mousemove event only fires if the cursor is on the actual form itself and not if its over a child control.
Does anyone know how to handle this event when there are an unknown number of child controls at design time?
Is there an easy one-liner I can use?
try this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
AddMouseMoveHandler(this);
}
private void AddMouseMoveHandler(Control c)
{
c.MouseMove += MouseMoveHandler;
if(c.Controls.Count>0)
{
foreach (Control ct in c.Controls)
AddMouseMoveHandler(ct);
}
}
private void MouseMoveHandler(object sender, MouseEventArgs e)
{
lblXY.Text = string.Format("X: {0}, Y:{1}", e.X, e.Y);
}
}
I know this post is quite old, but it seems to me that the simplest method would be for the form to implement IMessageFilter. In the constructor (or in OnHandleCreated) you call
Application.AddMessageFilter(this);
and then you can catch the messages of all windows in your implementation of IMessageFilter.PreFilterMessage.
You'd likely need to use P/Invoke for the WIN32 IsChild method
[DllImport("user32.dll")]
public static extern bool IsChild(IntPtr hWndParent, IntPtr hWnd);
along with the form's Handle property to ensure that you're handling the right messages.
imho there is a bit of a binary situation here : and there is no "one-liner." the only solution I can see is to get your controls that don't implement events into a .NET container that does.
When any control gets a click, the normal expected behavior is that it will become the Active Control of the Form (which can always be accessed by this.ActivceControl).
But, particulary if the control you clicked captures the mouse, something has got to raise an event since .NET does not implement event "bubbling" (as WPF does).
The usual way to deal with extending behavior of any object that is sealed or whatever is to write an extension method, and I have found writing extensions for Control quite easy, but I don't know if that will help you in this case. Unfortunately I am out of my home country right now, and do not have Visual Studio to play around with.
One strategy you can use to determine if a given Point on a Form falls within the bounds of any Control is to enumerate the areas (Bounds) of all controls on the Form via 'forall of the Forms Control.Collection (this.Controls). But, if you have overlapping Controls, you then have the issue of more than one control possibly containing a given point.
best, Bill
Why don't you just use the controls' mouseover event handlers?
I know I'm a bit late to the punch, but I was having troubles with this earlier today when using a Panel as a title bar. I had a label to display some text, a picturebox and a few buttons all nested within the Panel, but I needed to trap the MouseMove event regardless.
What I decided to do was implement a recursive method handler to do this, as I only had 1 level of nested controls, this may not scale overly well when you start approaching ridiculous levels of nesting.
Here's how I did it:
protected virtual void NestedControl_Mousemove(object sender, MouseEventArgs e)
{
Control current = sender as Control;
//you will need to edit this to identify the true parent of your top-level control. As I was writing a custom UserControl, "this" was my title-bar's parent.
if (current.Parent != this)
{
// Reconstruct the args to get a correct X/Y value.
// you can ignore this if you never need to get e.X/e.Y accurately.
MouseEventArgs newArgs = new MouseEventArgs
(
e.Button,
e.Clicks,
e.X + current.Location.X,
e.Y + current.Location.Y,
e.Delta
);
NestedControl_Mousemove(current.Parent, newArgs);
}
else
{
// My "true" MouseMove handler, called at last.
TitlebarMouseMove(current, e);
}
}
//helper method to basically just ensure all the child controls subscribe to the NestedControl_MouseMove event.
protected virtual void AddNestedMouseHandler(Control root, MouseEventHandler nestedHandler)
{
root.MouseMove += new MouseEventHandler(nestedHandler);
if (root.Controls.Count > 0)
foreach (Control c in root.Controls)
AddNestedMouseHandler(c, nestedHandler);
}
And then to set it up is relatively simple:
Define your "true" handler:
protected virtual void TitlebarMouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
this.Text = string.Format("({0}, {1})", e.X, e.Y);
}
}
And then set up the controls event subscribers:
//pnlDisplay is my title bar panel.
AddNestedMouseHandler(pnlDisplay, NestedControl_Mousemove);
Relatively simple to use, and I can vouch for the fact it works :)