I have a Control I want to create. Here's a simple example of what I was to accomplish.
I want the control to contain a button.
Button b = new Button();
b.Text = "Test";
b.Click += new EventHandler(b_Click);
this.Controls.Add(b);
Now, the control renders fine, the button shows up on the page. The heart of the problem I'm having is that the b_Click Event Handler is never triggered.
protected void b_Click(object sender, EventArgs e)
{
throw new NotImplementedException();
}
Any help here would be much appreciated. I don't want to use a User Control here for purely selfish reasons and would like to totally encapsulate this in a single DLL.
Thanks In Advance.
EDIT**
namespace ClassLibrary1
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:WebCustomControl1 runat=server></{0}:WebCustomControl1>")]
public class WebCustomControl1 : WebControl
{
protected override void CreateChildControls()
{
Button b = new Button();
b.ID = "button";
b.Text = "Click Me";
b.Click += new EventHandler(b_Click);
this.Controls.Add(b);
base.CreateChildControls();
}
protected void b_Click(object sender, EventArgs e)
{
this.Controls.Add(new LiteralControl("<p>Click!</p>"));
}
}
}
So from the comments I've tried this. The simplest of exampes, still no go. Is there something I'm fundamentally missing?
public class WebCustomControl1 : WebControl
needed to be
public class WebCustomControl1 : WebControl, INamingContainer
that's it. that's all that was needed to make this postback issue work.
Override the CreateChildControls method and create/add the button (and register the handler) in that method.
Using OnInit/OnLoad to create controls like this is incorrect and will lead to inconsistent behavior (such as what you're experiencing).
Edit: You might also try setting button.ID so that it's the same on every postback, it's possible the raised event isn't seeing it for that reason.
Please show the definition of your control class.
Does your control inherit from Control, WebControl, or CompositeControl. Try inheriting from CompositeControl and see if that helps.
So your button renders to the page correctly and causes a postback when the user clicks it, but the OnClick code never runs?
Remember, whenever you do a postback you're working with a brand new instance of your page class. Part of the postback process is running whatever event was triggered by the user. For this to happen, the asp.net processor needs to know about the event at a specific point in the page lifecycle. At this point, your button must already be on the page with the event handler registered. If this doesn't happen, your event won't fire. Try moving your button creation up earlier in the page lifecycle.
This may be the case of assigning the handlers well after the event is raised.
My common practice when dealing with controls are:
Use Control.Init event to initialize everything (default text, handlers, size, composition, etc.) and also initialize the child controls.
Use Control.Load event to handle any data binding operation (based on the properties/fields set during the Init event).
Check the identifier of the button control before the postback and after the postback. If it's not the same, it won't work.
You can overwrite the identifier yourself if needed.
Related
I have a WindowForm and some controls on it.
My point is that when I click button "?" on top-right of the datagridview, it will show a picture box and when I click outside the pictureBox, it must invisible.
My MainForm
MyPictureBox
I have searched some topics on this site, but some dont work, some work partly. Like
this.
I also tried:
void pictureBox1_LostFocus(object sender, EventArgs e)
{
if (pictureBox1.Visible)
pictureBox1.Visible = false;
}
But when I click on button2, button3, ... The pictureBox wasn't invisible.
Any solution will be highly appreciated.
I think your pictureBox1 isn't losing focus, cause it never actually GOT focused. Set it to be focused after making it visible.
Oh, I have encountered this before...
I was making a Label that you could double click and it would allow you to edit the Label.Text, like a TextBox. However, I was having problems hooking into the events to know when the user had clicked off the Control and wished to stop editing. I tried Control.LostFocus, and Control.Leave, but nothing. I even got frustrated/desperate and tried some silly ones like Control.Invalidated.
What I ended up having to do was subscribe to the Click event of the Form/Container/Control behind it.
However, putting the responsibility of wiring up this event into the Form that wants to use it is poor design. What you can do, however is to make the constructor to Control class require a reference to the owner/parent/container as a parameter. That way, the requirements are not hidden, they must be satisfied before you can get a object instance, and the control can wired up to the Form.Click within itself, where that logic belongs.
private Form owner;
public EditLabel(Form Owner)
{
this.owner = Owner;
owner.Click += EndEditing;
}
Add this method in designer.cs:
pictureBoxEvent this.MouseLeave += new EventHandler(pictureBox_MouseLeave);
Add this code in cs file:
private void pictureBox_MouseLeave(object sender, EventArgs e)
{
pictureBox1.Visible = false;
}
I have a Windows Forms Application. I have several forms in this application (a main form, and several specialized forms), and on only one form, click events are not firing for any of my buttons.
It is not that the code in the handler is broken. This can be determined by the fact that a breakpoint on the first line of the handler is never reached when clicking the button.
Other events are working (I'm using CheckedChanged events on this form and they are behaving).
My team members have reviewed, and also can't spot the problem.
Here is a simplified view of my code:
Designer Generated Code
partial class MyForm
{
private System.Windows.Forms.Button addButton;
private void InitalizeComponent()
{
this.addButton = new System.Windows.Forms.Button();
this.addButton.Name = "addButton";
// Drawing statements here
this.addButton.Click += new System.EventHandler(this.addButton_Click);
this.Controls.Add(this.addButton);
}
}
My Code
public partial class MyForm : Form
{
public MyForm()
{
InitializeComponent();
}
private void addButton_Click(object sender, EventArgs e)
{
MessageBox.Show("The debugger is not reaching a break point on this line");
}
}
Edit: Additional Information from Testing
There are several data-bound dropdownlists in my form. I have discovered that the click event only fails to fire if I make a selection in a drop down box first.
If I make no selections, the break point in the button's handler fires. Otherwise it doesn't. There are no events registered on these drop down lists.
Here is the reason:
When using data binding, when you enter a value in a data bound control, it first tries to validate entry and then if the entry was valid, data binding will put the value in data source, but if a validation error occurs validation returns false and your control goes to invalid mode.
When a child control of form didn't validate, by default you can not change focus from invalid control.
Click on a button by default causes validation of the control that are losing the focus, so you can't click on button, as you see your button reflect to mouse but not actually click.
The same problem will happen if you handle Validating event of a control like TextBox and set e.cancel = true.
Here is the fix:
you can fix this behavior using either of following options:
Set CausesValidation property of your button to false
Set AutoValidate property of your form to AutoValidate.EnableAllowFocusChange
This will do the trick for you
Change
public ScheduleMeeting()
{
InitializeComponent();
}
to
public MyForm()
{
InitializeComponent();
}
I have discovered the issue after further testing.
I the issue is not with button events, but with the form becoming blocked after making a selection from a drop down box.
I have not yet discovered why the form blocks after the drop down is selected (it has no events, but does have databinding, so there are some possible causes there).
Thank you for all your help!
I'm creating a custom dropdown box, and I want to register when the mouse is clicked outside the dropdown box, in order to hide it. Is it possible to detect a click outside a control? or should I make some mechanism on the containing form and check for mouseclick when any dropdownbox is open?
So I finally understand that you only want it to close when the user clicks outside of it. In that case, the Leave event should work just fine... For some reason, I got the impression you wanted it to close whenever they moved the mouse outside of your custom dropdown. The Leave event is raised whenever your control loses the focus, and if the user clicks on something else, it will certainly lose focus as the thing they clicked on gains the focus.
The documentation also says that this event cascades up and down the control chain as necessary:
The Enter and Leave events are hierarchical and will cascade up and down the parent chain until the appropriate control is reached. For example, assume you have a Form with two GroupBox controls, and each GroupBox control has one TextBox control. When the caret is moved from one TextBox to the other, the Leave event is raised for the TextBox and GroupBox, and the Enter event is raised for the other GroupBox and TextBox.
Overriding your UserControl's OnLeave method is the best way to handle this:
protected override void OnLeave(EventArgs e)
{
// Call the base class
base.OnLeave(e);
// When this control loses the focus, close it
this.Hide();
}
And then for testing purposes, I created a form that shows the drop-down UserControl on command:
public partial class Form1 : Form
{
private UserControl1 customDropDown;
public Form1()
{
InitializeComponent();
// Create the user control
customDropDown = new UserControl1();
// Add it to the form's Controls collection
Controls.Add(customDropDown);
customDropDown.Hide();
}
private void button1_Click(object sender, EventArgs e)
{
// Display the user control
customDropDown.Show();
customDropDown.BringToFront(); // display in front of other controls
customDropDown.Select(); // make sure it gets the focus
}
}
Everything works perfectly with the above code, except for one thing: if the user clicks on a blank area of the form, the UserControl doesn't close. Hmm, why not? Well, because the form itself doesn't want the focus. Only controls can get the focus, and we didn't click on a control. And because nothing else stole the focus, the Leave event never got raised, meaning that the UserControl didn't know it was supposed to close itself.
If you need the UserControl to close itself when the user clicks on a blank area in the form, you need some special case handling for that. Since you say that you're only concerned about clicks, you can just handle the Click event for the form, and set the focus to a different control:
protected override void OnClick(EventArgs e)
{
// Call the base class
base.OnClick(e);
// See if our custom drop-down is visible
if (customDropDown.Visible)
{
// Set the focus to a different control on the form,
// which will force the drop-down to close
this.SelectNextControl(customDropDown, true, true, true, true);
}
}
Yes, this last part feels like a hack. The better solution, as others have mentioned, is to use the SetCapture function to instruct Windows to capture the mouse over your UserControl's window. The control's Capture property provides an even simpler way to do the same thing.
Technically, you'll need to p/invoke SetCapture() in order to receive click events that happen outside of your control.
But in your case, handling the Leave event, as #Martin suggests, should be sufficient.
EDIT: While looking for an usage example for SetCapture(), I came across the Control.Capture property, of which I was not aware. Using that property means you won't have to p/invoke anything, which is always a good thing in my book.
So, you'll have to set Capture to true when showing the dropdown, then determine if the mouse pointer lies inside the control in your click event handler and, if it doesn't, set Capture to false and close the dropdown.
UPDATE:
You can also use the Control.Focused property to determine if the control has got or lost focus when using a keyboard or mouse instead of using the Capture with the same example provided in the MSDN Capture page.
Handle the Form's MouseDown event, or override the Form's OnMouseDown
method:
enter code here
And then:
protected override void OnMouseDown(MouseEventArgs e)
{
if (!theListBox.Bounds.Contains(e.Location))
{
theListBox.Visible = false;
}
}
The Contains method old System.Drawing.Rectangle can be used to indicate if
a point is contained inside a rectangle. The Bounds property of a Control is
the outer Rectangle defined by the edges of the Control. The Location
property of the MouseEventArgs is the Point relative to the Control which
received the MouseDown event. The Bounds property of a Control in a Form is
relative to the Form.
You are probably looking for the leave event:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.leave.aspx
Leave occurs when the input focus leaves the control.
I just wanted to share this. It is probably not a good way of doing it that way, but looks like it works for drop down panel that closes on fake "MouseLeave", I tried to hide it on Panel MouseLeave but it does not work because moving from panel to button leaves the panel because the button is not the panel itself. Probably there is better way of doing this but I am sharing this because I used about 7 hours figuring out how to get it to work. Thanks to #FTheGodfather
But it works only if the mouse moves on the form. If there is a panel this will not work.
private void click_to_show_Panel_button_MouseDown(object sender, MouseEventArgs e)
{
item_panel1.Visible = true; //Menu Panel
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (!item_panel1.Bounds.Contains(e.Location))
{
item_panel1.Visible = false; // Menu panel
}
}
I've done this myself, and this is how I did it.
When the drop down is opened, register a click event on the control's parent form:
this.Form.Click += new EventHandler(CloseDropDown);
But this only takes you half the way. You probably want your drop down to close also when the current window gets deactivated. The most reliable way of detecting this has for me been through a timer that checks which window is currently active:
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
and
var timer = new Timer();
timer.Interval = 100;
timer.Tick += (sender, args) =>
{
IntPtr f = GetForegroundWindow();
if (this.Form == null || f != this.Form.Handle)
{
CloseDropDown();
}
};
You should of course only let the timer run when the drop down is visible. Also, there's probably a few other events on the parent form you'd want to register when the drop down is opened:
this.Form.LocationChanged += new EventHandler(CloseDropDown);
this.Form.SizeChanged += new EventHandler(CloseDropDown);
Just don't forget to unregister all these events in the CloseDropDown method :)
EDIT:
I forgot, you should also register the Leave event on you control to see if another control gets activated/clicked:
this.Leave += new EventHandler(CloseDropDown);
I think I've got it now, this should cover all bases. Let me know if I'm missing something.
If you have Form, you can simply use Deactivate event just like this :
protected override void OnDeactivate(EventArgs e)
{
this.Dispose();
}
I have built an ascx control that is part of many different components of my application. There is a Previous and Next button on this control, which should be signaled to the parent aspx page. This is done by having the parent page add some Delegates for postbacks, such as OnPreviousClicked, OnNextClicked etc.
Everything in this app is 'ajaxified' with an updatepanel. Now I notice that my app breaks if I don't set the delegates on every single Page_Load call in the parent. In other words, if I don't ALWAYS set the delegates in the Page_Load of the parent aspx, then the ascx ends up with null delegates and an exception. Am I coding stuff correctly?
// inside the control
public event EventHandler OnPreviousClicked;
private void PreviousButton_OnClick(object sender, EventArgs e)
{
if(OnPreviousClicked != null) {
OnPreviousClicked(this, e); // or whatever args you want
}
}
// and inside the Page code-behind
private void Page_Load(...)
{
MyUserControl.OnPreviousClicked += new EventHandler(myHandler);
}
// OR inside the Page aspx, you also could just set the OnPreviousClicked property.
<xx:MyUserControl ID="MyUserControl1" runat="server" OnPreviousClicked="myHandler" />
See http://asp.net-tutorials.com/user-controls/events/.
If all you're doing is signaling the clicking of a button, I would have your ASCX control raise a simple event instead. That way each page can listen for the event if they need to and the controls can function regardless if anybody is listening.
First declare the events in your ASCX codebehind:
public event System.EventHandler NextSelected;
Then you create your button click events that raise the event.
protected void btnNextSelected_Click(object sender, EventArgs e)
{
if (EmployeeSelected != null)
{
NextSelected(this, new EventArgs());
}
}
Then in your parent ASP.Net pages you can add your ASCX control (we'll call it NavControl) and create methods that listen for these events.
NavControl.NextSelected += new EventHandler(NextPageRedirect);
protected void NextPageRedirect(object sender, EventArgs e)
{
Response.Redirect("~/ViewEmployee.aspx", false);
}
Note that with this you don't have to create the event handler or method and you can still use the nav control on your page. It should also eliminate the issues you are having with the delegates.
remember each postback has to restart your code. The event handlers don't get serialized to viewstate so you have to set them up again.
That is the expected behavior in asp.net.
The entire page life cycle occurs even when it is a partial postback (update panel). So in order for the events to fire, you will have to wire them up programmatically during page_load or declaratively in your markup (if those delegates you mentioned are events).
Say aspx page called theParent has a DataGrid control named theDataGrid and a UserControl named theUserControl , and theUserControl has a button named theUcButton .
Yes, I know, very imaginative naming.
When theUcButton is clicked , a session variable is changed.
This session variable is a select parameter for the datasource of theDataGrid.
However, because of the order of stuff called in the page lifecycle, when theUcButton is clicked and a postback is generated ,
theParent controls are loaded before theUserControl resets the session variable, and theDataGrid does not show the new data until the next postback .
How to I get what I want to work?
I am using 3.5 if that matters.
Here is example code for your user control to raise an event.
public partial class WebUserControl : System.Web.UI.UserControl{
public event EventHandler Updated;
protected void Button1_Click(object sender, EventArgs e)
{
//do some work to this user control
//raise the Updated event
if (Updated != null)
Updated(sender, e);
}}
Then from your .aspx page, you'll deal with the new event just like usual
<uc1:WebUserControl ID="WebUserControl1" runat="server" OnUpdated="WebUserControl1_Updated" />
Code behind:
protected void WebUserControl1_Updated(object sender, EventArgs e)
{
//handle the user control event
}
You can declare an event as a member of the UserControl class, raise it, and handle it in the Page class.
You can also use other events like Page.PreRender() to pick up things after the user control's events.
The easiest way would be to hold off on the databind until the later in the lifecycle, such as the Page.PreRender event as joelt suggested. If you do the bind then, you should have the session variables.