I'm working with a windows form app which I have treeview to show the list of folders , and I have attached NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) event.
and on click of node I call server method to populate the treeview.
Here I could see that NodeMouseClick for one of my tree node is not at all getting triggered.
however for rest of the nodes its working fine with no issues. can anyone tell me what is the exact reason that its not getting triggered.
and I dont want to use After_Select event.
public Form1()
{
InitializeComponent();
Init();
}
private void Init()
{
treeView1.Nodes.Add("root");
for (int i = 0; i < 23; i++)
{
treeView1.Nodes[0].Nodes.Add(i.ToString());
treeView1.Nodes[0].Nodes[i].Nodes.Add("child" + i.ToString());
}
treeView1.Nodes[0].Expand();
}
use treeview of size = 280,369
As I mentioned before in the comments, the workaround is to drop down to the level of the Windows API, intercept mouse messages, and raise the node click event yourself. The code is ugly, but functional.
Add the following code to a new class in your project (I called it CustomTreeView):
class CustomTreeView : System.Windows.Forms.TreeView
{
public event EventHandler<TreeNodeMouseClickEventArgs> CustomNodeClick;
private const int WM_LBUTTONDOWN = 0x201;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg == WM_LBUTTONDOWN) // left mouse button down
{
// get the current position of the mouse pointer
Point mousePos = Control.MousePosition;
// get the node the user clicked on
TreeNode testNode = GetNodeAt(PointToClient(mousePos));
// see if the clicked area contained an actual node
if (testNode != null)
{
// A node was clicked, so raise our custom event
var e = new TreeNodeMouseClickEventArgs(testNode,
MouseButtons.Left, 1, mousePos.X, mousePos.Y);
if (CustomNodeClick != null)
CustomNodeClick(this, e);
}
}
// call through to let the base class process the message
base.WndProc(ref m);
}
}
Then change all references to the System.Windows.Forms.TreeView control in your code to the new CustomTreeView class that you just created. This is a subclass of the existing TreeView control that you want to use instead. In case you're not familiar with subclassing, this is the way we modify the existing functionality, or bolt on new functionality to, an existing control. In this case, we've subclassed the original TreeView control to add the CustomNodeClick event that we'll be raising ourselves whenever we detect that a node has been clicked by the user.
Finally, change the event handler method in your form class to listen for the CustomNodeClick event that we're raising, rather than the buggered NodeMouseClick event you were trying to use before.
Compile and run. Everything should work as expected.
try to use AfterSelect Event it must be triggered after any node selection .
Related
I have a custom control (custom ComboBox). It`s work well, when I press "arrow button" it deployed, if I press it again it deployed too, but if it are deployed and I press anywhere in my form - it close but then, when I'm trying to open it - I must press "arrow button" two times. So I need to detect this moment, when I click outside of my combobox.
Code to open ComboBox(call in ButtonClick)
private void OpenComboBox()
{
if (drop_flag)
{
...
popup.Show(this);
}
else
{
drop_flag = true;
}
}
And Close event
private void popup_Closed(object sender, ToolStripDropDownClosedEventArgs e)
{
drop_flag = false;
}
So, I want something like this
private ClickedOutsideControl()
{
dropflag = true;
}
Thats not that easy.With basic .net you always need some kind of control / references to intercept clicks by binding click events. For Example if you have an MDI Parent (Mainwindow you can intercept its clicks).
Another way (using windows api) is to hook inso system events See the following stackoverflow post Global mouse event handler.
But you should think twice if you really need this.
In the form, you can pre-process messages sent to controls by using a message filter. Here is my FAILED attempt to implement the desired functionality:
public partial class frmAutoCloseDropDown : Form, IMessageFilter
{
int _lastMsg;
public frmAutoCloseDropDown()
{
InitializeComponent();
Application.AddMessageFilter(this);
}
// THIS ATTEMPT DOES NOT WORK!
public bool PreFilterMessage(ref Message m)
{
const int WM_LBUTTONDOWN = 0x0201;
if (m.Msg!= _lastMsg) {
_lastMsg = m.Msg;
}
if (m.Msg == WM_LBUTTONDOWN) {
// You would have to do this recursively if the combo-boxes were nested inside other controls.
foreach (ComboBox cbo in Controls.OfType<ComboBox>()) {
cbo.DroppedDown = false;
}
}
return false;
}
// Note: Dispose is created inside *.Designer.cs and you have to move it manually to *.cs
protected override void Dispose(bool disposing)
{
if (disposing) {
Application.RemoveMessageFilter(this);
if (components != null) {
components.Dispose();
}
}
base.Dispose(disposing);
}
}
Why does it not work? Probably because the new messages generated by cbo.DroppedDown = false that are sent to the form to close the drop-down are appended to the message queue and are only processed after the left mouse button click has been processed. This means that even when with PreFilterMessage our attempt to close the drop-down comes too late.
A possible solution would be to re-send WM_LBUTTONDOWN to the right ComboBox. You would have to interpret the parameters of the message to get the mouse coordinates and look to which combobox they are pointing to get its HWnd. My attempt to do this also shows that the behavior also depends on the drop-down style. It also has the undesired effect to close the drown-down you just opened. And what happens if you click in the drop-down itself to select an entry?
Probably you could do it, but the code tends to become very complicated. It's not worth the effort.
When coding a small game, I encountered a problem; my form's KeyDown and KeyUp events don't fire at all.
This is the form's code:
public class GameForm : Form
{
private ControllableBlock player;
public GameForm()
{
KeyDown += Game_KeyDown;
KeyUp += Game_KeyUp;
player = new ControllableBlock();
Controls.Add(player);
}
private void Game_KeyDown(object sender, KeyEventArgs e)
{
player.ReactToKey(e.KeyCode);
}
private void Game_KeyUp(object sender, KeyEventArgs e)
{
player.ReactToKey(e.KeyCode);
}
}
There's a lot more going on, but I only pasted the relevant code.
I've already tried setting this.KeyPreview = true; and calling this.Focus();, neither works.
The problem is not in ReactToKey() method, I've already set a breakpoint there and the event is never fired.
Edit: After some tests I've come to a conclusion that the problem is within my ControllableBlock.
Yet, I have no idea why, but I'm working on it.
If I comment out everything that's related to the player, the events start firing.
Edit 2: Seems like the problem is me inheriting my ControllableBlock from Control.
If I inherit it from Panel, it works fine.
Why is this?
Can't I fire an event if I inherit from control?
The ControllableBlock class is empty for now, so it doesn't even do anything other than inherits from Control.
Edit 3: Now that I've started a bounty, I'd like to clarify that I'm not looking for a solution on how to make the events fire, I'm looking for a reason on why they don't fire if I inherit from Control.
If your events should be application-wide try to set property KeyPreview to true - it will allow you to fire respective events regardless of focused control.
this.KeyPreview = true;
Otherwise you should attach these events directly to control that will process them.
Edit:
I removed InitializeComponent(); from my form and got behaviour identical to yours.
After implementing solution provided in this question all events started to qork perfectly.
Copy code snippet here:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (keyData == Keys.Left) {
// Do your staff for Left Key here
return true;
}
// you need to add if condition to every direction you want to handle
return base.ProcessCmdKey(ref msg, keyData);
}
I was able to reproduce a similar issue (which is hopefully related..)
Explanation:
Controls which return CanSelect==true are selectable for keyboard input
A blank descendent of Control() is selectable, one of Panel() is not
The first selectable control added to a form will get selected
A selected control will steal keyboard events from its parents by default
Certain keys used for navigation within a window require extra steps to be handleable
Check here for a good overview of how windows keyboard input works.
Code to reproduce it:
public class GameForm : Form
{
public GameForm()
{
this.KeyDown += Game_KeyDown;
var tests = new List<Control[]>() {
new[] { new Panel() },
new[] { new Panel(), new Panel() },
new[] { new Control() },
new[] { new Control(), new Panel() },
new[] { new Panel(), new Control() }
};
// When test index 2 to 4 used, keyboard input does not reach form level
Controls.AddRange(tests[0]);
// When uncommented, ensures all keyboard input reaches form level
/*this.KeyPreview = true;
// Additional plumbing required along with KeyPreview to allow arrow and other reserved keys
foreach (Control control in this.Controls)
{
control.PreviewKeyDown += control_PreviewKeyDown;
}*/
}
void control_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
e.IsInputKey = true;
}
private void Game_KeyDown(object sender, KeyEventArgs e)
{
// breakpoint here
Debug.WriteLine(e.KeyCode);
}
}
You need to make your control selectable before it can receive the focus.
Try adding the following to your constructor:
this.SetStyle(ControlStyles.Selectable, true);
And ensure that you give your form focus after it has been displayed. Or, override OnMouseDown() and call this.Focus() in it.
try moving the handler setup to the Form_Load event rather than the constructor. Should there not be a call to Initialize() in the constructor? I wouldn't particularly recommend removing it
If ControllableBlock inherits from Panel, it will have more event hookups and better UI interaction setup than a base Control object.
I'm working with a C# WinForm. It has more than a dozen text boxes, combo boxes, and check boxes. The winform displays information that is retrieved from a database. There is a save button on the form that is disabled. I want to be able to enable it when any of the text boxes/combo boxes/ check boxes are changed.
Is there an easy to way to do this without adding separate event handlers to each of these items?
Here is enough to get you stared. You may need to add extra foreach loops for other control types as needed. The nice thing is that you only need a few lines of code per Control type, not per instance, with this approach.
private void addHandlers()
{
foreach (TextBox control in Controls.OfType<TextBox>())
{
control.TextChanged += new EventHandler(OnContentChanged);
}
foreach (ComboBox control in Controls.OfType<ComboBox>())
{
control.SelectedIndexChanged += new EventHandler(OnContentChanged);
}
foreach (CheckBox control in Controls.OfType<CheckBox>())
{
control.CheckedChanged += new EventHandler(OnContentChanged);
}
}
protected void OnContentChanged(object sender, EventArgs e)
{
if (ContentChanged != null)
ContentChanged(this, new EventArgs());
}
public event EventHandler ContentChanged;
After modifying the addHandlers method to support all of your controls, and calling it after adding all of the controls to your form, you can simply subscribe to the ContentChanged event for doing whatever might need to happen anytime something on the form changed (i.e. enable/disable a save button).
You have to go through through the controls and attach change event to every control. This article discuss the similar situation.
private void AssignHandlersForControlCollection(
Control.ControlCollection coll)
{
foreach (Control c in coll)
{
if (c is TextBox)
(c as TextBox).TextChanged
+= new EventHandler(SimpleDirtyTracker_TextChanged);
if (c is CheckBox)
(c as CheckBox).CheckedChanged
+= new EventHandler(SimpleDirtyTracker_CheckedChanged);
// ... apply for other desired input types similarly ...
// recurively apply to inner collections
if (c.HasChildren)
AssignHandlersForControlCollection(c.Controls);
}
}
You can write a general purpose event handler that handles the event for all of them, but alas, no, there is no way to autogenerate event handlers in Visual Studio (nor should there be).
You can have all of the events hooked up to one handler, just have them all call the same function, there you have a bool flag mbSomethingChanged = true and enable the save button. Check the flag on form close, if you want to alert the user to save.
I have a windows form with a panel on the left, which consists purely of radiobuttons, and a tabcontrol in the middle, with multiple tab pages within it. Each of these individual tabpages have a series of datagridviews within it, which are shown and hidden depending on which radio button you check.
I accomplish this effect by having each of the radiobuttons on the left assigned a CheckChanged event, which loops through all of the controls within the tabpagecontrol.SelectedTab, and calls .Show() on the corresponding datagridview and calls .Hide() on the rest so that only one datagridview is visible at one time.
My problem occurs when i try to programmatically check one of these RadioButtons. Lets say in Method X, I write RadioButtonA.checked = true. This triggers the usual CheckedChange event handling, which loops through all the datagridviews on the currently selected tabpage and calls .Hide() on everything except the one datagridview form that the radiobutton is supposed to bring up and calls .Show() instead. However, on one of these .Hide() calls on the datagridview, it ends up triggering the RadioButtonA.CheckedChange event AGAIN for a second time. When i look at the sender argument passed to the function, it shows that the sender is the RadioButton i just programmatically clicked on.
I am adding these datagridviews programmatically and can confirm that there are no eventhandlers assigned whatsoever to them. Can anyone help me determine what is causing this additional event to get triggered? Thanks.
For obnoxious change events that trickle through and upset other event handlers on my forms, I've found the only solution is to add a small boolean value:
bool radioIng;
void MyMethod() {
radioIng = true;
try {
radioButton1.Checked = true;
// etc.
} finally {
radioIng = false;
}
}
void radioButton_EventHandler(object sender, EventArgs e) {
if (radioIng) return;
// rest of code here
}
EDIT:
Alternately, you could just remove all of your event handlers and reconnect them later:
void MyMethod() {
try {
radioButton1.CheckChanged -= radioButton_EventHandler;
radioButton2.CheckChanged -= radioButton_EventHandler;
radioButton3.CheckChanged -= radioButton_EventHandler;
// execute your code
radioButton1.Checked = true;
} finally {
radioButton1.CheckedChanged += new EventHandler(radioButton_EventHandler);
radioButton2.CheckedChanged += new EventHandler(radioButton_EventHandler);
radioButton3.CheckedChanged += new EventHandler(radioButton_EventHandler);
}
}
void radioButton_EventHandler(object sender, EventArgs e) {
if (sender == radioButton1) {
// code here to handle
} else if (sender == radioButton2) {
// code here to handle
} else if (sender == radioButton3) {
// code here to handle
}
}
I have a webusercontrol with a few controls on it like some labels,a textbox and eventually a button. The purpose of this control is to add it to my main page in a placeholder every time I click on the button on the webusercontrol.
This is the code behind my button on my webcontrol
protected void btnCriteriaToevoegen_Click(object sender, EventArgs e)
{
//New eventhandler == all of the eventhandlers of all the objects who have subscribed to the event.
EventHandler eventhandler = ButtonDoorgaan;
ButtonOpslaanEvent mijnevent = new ButtonOpslaanEvent();
//Basic variables I will give with my costum event(ButtonOpslaanEvent)
mijnevent.Naam = txtCriteriumNaam.Text;
mijnevent.Score = Convert.ToInt16(DdlCriteriumScoreSchaal.SelectedValue);
int weging = Convert.ToInt16(DdlCriteriumWeging.SelectedValue) - 1;
mijnevent.Weging = Convert.ToInt16(weging);
//If the eventhandler is not null, for every object that has an eventhandler, execute it.
if(eventhandler!=null)
eventhandler(sender, mijnevent);
}
The eventhandler that need to be executed when the event is fired is defined in my main page like this :
private void critlijn_ButtonDoorgaan(object sender, EventArgs e)
{
ButtonOpslaanEvent eigenevent = (ButtonOpslaanEvent)e;
IEnumerator<Domein> domeinenumerator = domeinen.GetEnumerator();
while (domeinenumerator.MoveNext())
{
if (domeinenumerator.Current.DomeinNaam.Equals(lijstdomeinitemgeselecteerd))
{
Criterium nieuwcriterium = new Criterium();
nieuwcriterium.CriteriumNaam = eigenevent.Naam;
nieuwcriterium.CriteriumScore = Convert.ToString(eigenevent.Score);
nieuwcriterium.CriteriumWeging = Convert.ToString(eigenevent.Weging);
domeinenumerator.Current.Criteriums.Add(nieuwcriterium);
}
}
btnCriteriaToevoegen_Click(sender, e);
}
The btnCriteriaToevoegen_Click event fires and then calls this method(addCriteriaButton()), which will add the button onto the placeholder in my main page:
private void addCriteriaButton()
{
Criterialijn criterialijn = (Criterialijn)LoadControl("~/Criterialijn.ascx");
//Add eventhandlers to control
criterialijn.ButtonDoorgaan += new EventHandler(critlijn_ButtonDoorgaan);
criterialijn.Aangevinkt += new EventHandler(critlijn_Aangevinkt);
//Every control on the page except this one, not enabled
IEnumerator<Criterialijn> criterialijnenumerator = criteriacontrols.GetEnumerator();
while (criterialijnenumerator.MoveNext())
{
criterialijnenumerator.Current.Enabled = false;
}
//Add it to a list of webusercontrols that are currently on screen
criteriacontrols.Add(criterialijn);
criterialijn.Enabled = true;
//Add to placeholder
plhCriteria.Controls.Add(criterialijn);
}
So when all this is said and done, and I run my program, he adds the control to my placeholder, but when I click on the button, he does not add a new control to my placeholder, and just clears my placeholder for some reason. Normally everything should be fine, but I have tried to see if he actually fires the event when you click on the button, and he does not. I have tried to give you a sample of my code, because the code of the whole page is quite big and that would not help you at all. Any ideas why he is not firing the event of the button?
So when your button that you dynamically added posts back, a new page instance is created and that button no longer exists (since you only added it on the previous button click), it has not been recreated.
You must re-create dynamic controls on each postback
Remeber, a new instance of the Page class is created for each postback, any previously created controls, event handlers will not exists in the new instance unless you explicitly re-create them.
I assume these Criteria are some sort of tree structure the user can navigate through (and hopefully arriving at the end somewhere ?).
About btnCriteriaToevoegen_Click:
Why are you defining an event inside a method?
In critlijn_ButtonDoorgaan and addCriteriaButton:
Instead of using an enumerator, just use
foreach(var control in criteriacontrols)
control.Enabled = false;
So yeah, fair to say it's still not quite comprehensable, but it least I tried right? :)
EDIT
ok, then I have this question:
The eventhandler that need to be
executed when the event is fired is
defined in my main page like this :
How sure are you that, when you do
EventHandler eventhandler = ButtonDoorgaan;
the variable "eventhandler" gets all eventhandlers attached to ButtonDoorgaan ?
EDIT 2 (the return)
See Richard Friend's answer; your control is not there anymore