In .NET C# 3.5 Winforms, I have a user control with some simple child controls such as textboxes, labels, and buttons. Currently when I set the .Enabled property of the user control to false, the controls dim accordingly. However, if I use a custom .BackColor for the user control, sometimes the dimming is not as apparent as I would prefer.
Is there a way to specify or change the dimming color of the user control when .Enabled is set to false? Or on a related note, is there a way I can call a method when this happens?
Controls have an EnabledChange event you can tap into. Create a handler for this event for the user control and change its controls' properties accordingly.
You can override .OnEnabledChanged(EventArgs e) method if you dont want to subscribe to EnabledChanged event, and it's a better solution than hiding Control's .Enable property, which is not marked virtual:
protected override OnEnabledChanged(EventArgs e)
{
base.OnEnabledChanged(e);
// your code here
}
I wound up overriding the base property of the user control, because I wanted the code that handles the state change to be in the user control itself (rather than subscribing to an event).
This is what I did:
public new bool Enabled
{
get
{
return base.Enabled;
}
set
{
base.Enabled = value;
// code to alter the appearance of control
}
}
EDIT:
The suggestion of self-subscribing to the even within the user control seemed much cleaner than hiding the non-virtual Enabled property. Further edits to other answers gave me this better solution:
this.EnabledChanged += new EventHandler(UserControl_EnabledChanged);
void UserControl_EnabledChanged(object sender, EventArgs e)
{
// code to alter appearance of control
}
Related
The short version:
Is there a way I can add properties and override the OnMouseDown / OnMouseUp functionality on the DataGridView control without creating my own control which extends the DataGridView?
The long version (with an explanation):
I am implementing drag and drop moving of multiple rows between grids in an existing application. I have an extended DataGridView control with the functionality I require, and it works perfectly moving rows between instances of this grid.
This is the extended DGV class code:
public partial class DragDropGrid : DataGridView
{
/// <summary>
/// When set, the mouse down event and click events don't happen until the mouse button is released.
/// </summary>
public bool DelayMouseDown = false;
public int MouseDownRowIndex = -1;
protected override void OnMouseDown(MouseEventArgs e)
{
if (DelayMouseDown)
{
return;
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (DelayMouseDown)
{
base.OnMouseDown(e);
}
base.OnMouseUp(e);
}
}
However, I also have another grid (a custom UserControl) which I need to be able to handle the same drag and drop functionality. This grid is a composite user control from our base class suite, and has a DataGridView along with a bunch of other stuff sitting on the control.
I have tried the following to implement the same functionality as the base DataGridView I have extended successfully, but have had no luck getting it to work:
Extending my base class grid the same as the DataGridView. This
doesn't work as overriding the MouseDown / MouseUp methods does so on the
custom UserControl, not the DataGridView which sits on the control - so
clicking the grid doesn't fire the overridden methods.
Updating the base class grid with the properties / overridden methods.
This has the same problem as above; the MouseDown / MouseUp is
overriding the UserControl's methods, so clicking the DataGridView on
the control doesn't hit the overridden methods.
I was hoping to just add the properties and override the methods on the whole DataGridView class, so that the DGV on the custom UserControl would pick up the functionality alongside the other DataGridView controls.
Help!
It seems that your user control has a DataGridView inside anyway.
So instead of binding the existing MouseDown/MouseUp events etc. (which indeed are events of the user control, not the grid), just add new events to your user control, and let them pass down to the grid view. For example:
public event MouseEventHandler GridMouseDown
{
add { dataGrid.MouseDown += value; }
remove { dataGrid.MouseDown -= value; }
}
EDIT:
Okay, since you ask specifically about overriding the OnMouseDown methods, the answer is simple: creating a derived type is the only way.
There are some very dirty hacks you could use to get around this, but that's a bad idea for something as trivial as this.
I have a disabled from element and I want to enable it on double-click.
The problem is the DoubleClick handler only gets called when Foo.Enabled = True. When it's disabled, the handler doesn't received the double-click event.
this.Foo.DoubleClick += new System.EventHandler(this.Foo_OnDoubleClick);
// Handler
private void Foo_OnDoubleClick(object sender, EventArgs e)
{
Console.WriteLine("Double click");
}
Is there any workaround to this?
Create your own control inheriting from its base control (FOO) and override the Enable behavior. That way you will be able to make it behave the way that you wish.
EXAMPLE:
public class MyControl : Button //Example control
{
//Override and/or implement what you need in this control
public MyControl()
{
}
protected override void OnEnabledChanged(EventArgs e)
{
// Do whatever you wish to do
}
}
I still want to prevent users from entering a value when it is disabled.
Your question makes it sounds like you want to apply this to any control, but then your comment makes it sounds like you want to apply it to a TextBox.
If the latter case is true, set the TextBox.ReadOnly property to True instead of disabling the control.
Users will not be able to edit the value, but the double-click event will still fire.
You Should handle mouse double click event of Parent(all double click goes to Enabled parent) then by check mouse location returned by event against each disabled control rectangle you can determine which control has been clicked.
The child controls of my custom control are obstruction the mouse events in my custom control. I have worked through the accepted answer and the answer at the bottom of this thread...
exposing-events-of-underlying-control
I haven't gotten them to work (the answer at the bottom seemed most straight forward to me). But really I would like to disable the events of them altogether. I have a pictureBox and a label, I don't need to interact with either of the child controls. Is there a way to disable them so they wont interfere with the events of my custom control?
Edit:
I'm using the custom control to gather and process a number of things and make them available as properties. When I click on it, I need to access to the properties. When the event happens at the child control, I don't have access to the propertied of my custom control. The following code is in my form...
public void Form1_MouseDown(object sender, MouseEventArgs e)
{
var myControl = sender as SubstanceViewer;
richTextBox1.Text = myControl.substanceInfo;
}
so I will need to access the properties of the parent control.
If you need the the events that are normally trapped by the child controls to be handled by the custom control itself, then simply wire up those events at run-time in the constructor of the custom control.
For example if you needed the MouseMove() event of the PictureBox and Label to fire the already wired up event of the UserControl:
public partial class SomeUserControl : UserControl
{
public SomeUserControl()
{
InitializeComponent();
this.pictureBox1.MouseMove += SomeUserControl_MouseMove;
this.label1.MouseMove += SomeUserControl_MouseMove;
}
private void SomeUserControl_MouseMove(object sender, MouseEventArgs e)
{
}
}
Be aware, though, that since different controls are firing the same handler you'll need to take that into account. For example, the e.X and e.Y values in the handler above would be relative to the source control.
*You can also wire these events up at design-time using the IDE itself, but I thought code better illustrated the solution.
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 got a form with a number of buttons on it (assume 20). In the middle, I got a User Control which is completely empty. The question is: how can I make it so that when the User Control is clicked, it will get keyboard focus?
Reason: I paint shapes in that User Control, with my mouse. The shapes are actually other User Controls. What I want to do is be able to use the keyboard to move those shapes. But I cannot seem to correctly be able to grab the Keyboard focus. The Key_Down events just don't reach my main (drawing into) User Control.
So, in other words, how can we have keyboard focus in a control has no focusable items on it? How can one make an keyboard-unfocusable control, catch those events? Any way of grabbing these events window-wide, other than going raw-WIN32 API hardcore?
A UserControl was very much designed to be a container control for other controls. It abhors getting the focus and tries to pass it off first chance it gets. You should not be using a UserControl here, given that you don't put any controls inside of it. A Panel control will suffice. Which has the exact same problem, it doesn't want to get focus either.
Surgery is required to override its behavior. Everything you need is in this answer.
Add this to your user control code to capture keyboard input:
/// <summary>Keys which can generate OnKeyDown event.</summary>
private static readonly Keys[] InputKeys = new []
{ Keys.Left, Keys.Up, Keys.Right, Keys.Down, };
protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
{
if(Array.IndexOf<Keys>(InputKeys, e.KeyCode) != -1)
{
e.IsInputKey = true;
}
base.OnPreviewKeyDown(e);
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
// just to illustrate this works
MessageBox.Show("KeyDown: " + e.KeyCode);
}
See http://msdn.microsoft.com/en-us/library/aa969768.aspx. You can assign keyboard focus by
1. Set the usercontrol.Focusable=true;
2. Use Keyboard.Focus(usercontrol).
You need to set the CommandRouting flag on your control to true.
The command routing dependency property is defined in a public API (MS.VS.Editor.dll) and your adornment will want to use that API to indicate that it is handling its commands instead of allowing the containing text view handle them. You can do this with from your control’s initialization.
Microsoft.VisualStudio.Editor.CommandRouting.SetInterceptsCommandRouting(this, true);