TreeView EndUpdate() call isn't working as I thought it would - c#

So I have a tree view that I extended so the user could select multiple items, but I've run into a little bug where it randomly stop painting itself.
After some digging around I actually found that it was my MouseDown and MouseUp events that were causing the problem:
protected override void OnMouseDown(MouseEventArgs e)
{
BeginUpdate();
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
EndUpdate();
base.OnMouseUp(e);
}
So if the user clicks in the TreeView, but releases outside of it, the MouseUp event doesn't get called, and the update won't end. I understand that much, but what's strange is after that, no matter how many times they click in the box it won't start drawing again, even though the EndUpdate() is now being called. This can be resolved by adding a button with EndUpdate() in it, but obviously I don't want that.
The reason I even need this is that I manually manage the coloring of the nodes as well as the selected node so all of the normal selection events are actually canceled. But without the Begin and End update I still get a flicker of a selection box before it reaches the cancel. With these I don't get the flicker but apparently it comes at a cost.
Here's a dropbox link to an empty project that demonstrates the issue.
If you break on the end update, you can see that it is getting called, but it just doesn't seem to change anything. But if you click the button that calls the same code, it resolves it.
Can anyone explain why this is happening? Am I missing something terribly obvious? Or is this just a bad way to go about it in general?
Regards.
Answer:
As no answer has been submitted, here's the solution brought to my attention by Hans Passant. Didn't want to post and mark as answer myself as I didn't come up with this.
Added this to my extended TreeView class and all issues seem to be resolved. (Albeit it's probably going to make some bad calls, but besides being wasteful it doesn't appear to hurt anything).
protected override void OnMouseCaptureChanged(EventArgs e)
{
EndUpdate();
base.OnMouseCaptureChanged(e);
}

Related

C# Winforms Event is adding _1 at the end of everything

My project became corrupt today and since fixing i have noticed that at the end of events like button click its adding _1 at the end of everything
for example
private void createNewUserToolStripMenuItem_Click_1(object sender, EventArgs e)
{
}
where as it should obviously be
private void createNewUserToolStripMenuItem_Click(object sender, EventArgs e)
{
}
I have no idea what has happened but its causing me to repeat a lot of work. I have not duplicated controls or anything like that so i am very confused
Does anyone have any ideas? Please this is over 2 years of work and its looking like its knackered
Thanks to Idle_Mind this worked -
The designer lost the fact that the control was wired up and when you double click it it sees that there is already a method with that name so it adds one. All you need to do is select that control, go to the Events listing in the Properties pane, find that event, and then change the listed handler using the dropdown back to the original method. –

DataGridViewButtonColumn Handler Never Called

I am creating a simple Windows Form with a DataGridView that contains a total of 6 columns. Only one of these columns (the very first) is a button column. I am having trouble with the handler for when the button, or rather, cell is clicked.
For reference (if it matters), the name of my DataGridView is infoGrid.
The only operation that I have the handler perform is a simple popup window, just so I can know the handler is being correctly called. I also have not added conditions yet to ensure it was the first column that was clicked, but I do plan on implementing conditions when I can get the handler to properly work. This is what I have:
private void infoGrid_CellClick(object sender, DataGridViewCellEventArgs e)
{
MessageBox.Show("It's working.", "Test", MessageBoxButtons.OK);
}
After spending a couple of hours scouring the internet for an answer, I noticed that just about all examples on this website and others use dataGridView1_CellClick for their method name. I wasn't sure if that was just a generic answer, but I tried it myself, and still got nothing.
I added breakpoints at the beginning of this method as well, and found that no matter how many times I click ANY cell, the handler is never even called.
Does anybody know what I may be doing wrong? All help is appreciated!
CDove actually answered this for me in his comment to the original question, but I just wanted to make it more visible for anyone else who may have the same problem.
After creating the handler method, you have to go to the designer and click the lightning bolt "Events" button under the "Properties" side bar. Then find the event you want to handle, and add the handling method that you created to it.
Once again, credit for this answer goes to CDove.

Is there a similar way to Form.Shown later in a form's lifetime?

Understanding that I can use the Forms.Shown event to be notified when a form fully is shown for the very first time, I found no way yet to get something similar later in the form's lifetime.
I.e. consider adding/removing controls or resizing the form.
I thought about registering for the Application.Idle event and somehow connect this to my form but I'm unsure whether this is the right way to go.
In addition I thought about subclassing the LayoutEngine class to gain access to some events/overrides. Again this doesn't look the right way to go for me.
So my question is:
When programmatically adding/removing/resizing a control when a form is already visible, how can I detect when the form is fully finished layouting and drawing again?
I.e. consider adding/removing controls or resizing the form
No, you never want to do this when the user can see the window. There are two basic places where you want to write code like this before the user can see you monkeying with the window, actually seeing it changing size, moving around and generally flickering madly because of multiple Paint events.
First and foremost is your class constructor. That's where you initialize the object in .NET, adding or removing controls belongs there.
Secondary is the Load event. An event that's generally very poorly understood and gets used way to often in Winforms. Induced by it being the default event for the Form class, an historical accident that goes back to early versions of Visual Basic where it was the event to use to initialize the form. That is no longer appropriate, .NET class objects should be initialized in their constructor.
Load, or rather OnLoad(), is appropriate when your code needs to know the Location or Size of the window. Everything that needs to happen to the native window is done by then. It has a valid Handle property, Windows picked a Location, it got rescaled if necessary to accommodate the machine's DPI setting, its Size property is now valid, reflecting scaling and any user preferences, layout was performed so every control is in its expected place. The only thing that didn't happen yet is that the Paint event wasn't fired yet. So the window is not yet visible to the user. That happens very soon after Load completes.
So sizing or moving the window is typically appropriate in OnLoad, given that you now know the real size and location. Do beware DPI rescaling, never hard-code a size.
If you need code run some fixed time after the window is displayed then a Timer is appropriate.
I'm taking a guess here, but the form's OnPaint event might do the trick. You can just override the base OnPaint handler, call the parent function, and put any logic you have after that.
You could use separate events call for each of the events that call a common function
I.E. ResizeEnd for resizing, ControlAdded and ControlRemoved for the controls.
private void Form1_ResizeEnd(object sender, EventArgs e)
{
DoWork()
}
private void Form1_ControlAdded(object sender, ControlEventArgs e)
{
DoWork()
}
private void Form1_ControlRemoved(object sender, ControlEventArgs e)
{
DoWork()
}
private void DoWork()
{
//What needs to be done for each of them
}
Im not sure about the timing of these calls though.

Win32Exception - error creating window handle?

Can you explain why this happens? Here are steps to reproduce the exception:
Drag-n-drop a TextBox on a form. Add any other focusable control such as a button on the form.
Add 2 event handlers for that TextBox as follow:
private void textBox_GotFocus(object sender, EventArgs e){
((TextBox)sender).HideSelection = false; //<-- exception highlighted at here.
}
private void textBox_LostFocus(object sender, EventArgs e){
((TextBox)sender).HideSelection = true;
}
Run the form and first click on the textBox, then click on the button then click on the textBox again, and the exception will throw: "Win32Exception - error creating window handle".
The code is simply to make the HideSelection change accordingly when the textBox gets focused and loses focus.
UPDATE
I don't know why is so volatile, I created another project and now the problem is different, there is no exception but an indefinite loop which makes the textbox flicker, the form doesn't seem to responsive, the CPU usage is consumed up to about 17-20%. The last demo is still opened and that demo still has the Win32Exception thrown. Not a clue at all. Now the code of the two projects are the same but the problems are different.
Well, there are a couple of things to note.
First of all, HideSelection doesn't hide or unhide the selection. It specifies, whether the selection will be hidden (or not) when the control loses focus. So it's pointless to change it when the TextBox becomes focus'ed.
What you're doing in LostFocus is default, btw. Why there is an exception in GotFocus is burried within Windows API, I guess. Maybe some undefined state when HideSelection checks inside a focus changed event whether the TextBox has focus or not or tries to hide the selection which is not shown.
Edit: It's not the Windows API on the first hand, but the framework. It tries to "recreate the handle" in the setter of HideSelection if it is changed (don't know why yet - would have to analyse sources) and seems to fail (don't know why either).
Edit2: Finally there's some problem in Win32 DestroyWindow - which leads to skipping the creation of the new window. Maybe because the old one is "in use" in the focus change events?
Interestingly, as soon as the exception occurs (for me), the LostFocus event is fired, immediately followed by GotFocus which throws another exception a.s.o. thus blocking the GUI. Both assignments to HideSelection throw the exception.
Also, when you click the TextBox, any selection is deselected automatically. This, however, is not the reason for the problem, because the exception is thrown if you change focus by pressing Tab (whose normal behaviour is to restore focus). But it may be related (state problems).
If you actually want to restore the selection, you could do it like this:
int selStart;
int selLen;
void textBox1_LostFocus(object sender, EventArgs e)
{
selStart = textBox1.SelectionStart;
selLen = textBox1.SelectionLength;
}
void textBox1_GotFocus(object sender, EventArgs e)
{
BeginInvoke((Action)(() =>
{
textBox1.SelectionStart = selStart;
textBox1.SelectionLength = selLen;
}));
}
I repro this crash. You can easily see it going wrong by setting breakpoints on your event handlers, note how they fire over and over again before your program bombs. The explanation is a bit long winded, I'll give the short version first. The MSDN documentation for the LostFocus event gives stern warnings, both a Note and a Caution, pointing out that this is a low-level event that is dangerous. These events are also hidden in the Properties window for that reason. Fix your problem by using the Enter and Leave event instead.
The long version: the TextBox.HideSelection property is rather special. It is related to the way some properties on native Windows controls are specified. These controls are created with the CreateWindowEx() winapi function, it takes a dwExStyle and dwStyle argument, flags that specify style options for the window. The HideSelection property is such a style flag, ES_NOHIDESEL.
That presents a problem when you want to change the property. Difficult because it can only be specified when the native control is created. Winforms does something pretty heroic to deal with that limitation, it destroys the native control and re-creates it.
That can have pretty interesting side effects, to put it mildly. Most are not observable, but you do for example see the window on the screen getting destroyed and recreated. That's why it flickers. The core problem with your code is, inevitably, because the native window is getting destroyed it also loses the focus. So the LostFocus event immediately fires, right after you got the GotFocus event. Which does something unfortunate, it again changes the HideSelection property. Which forces Winforms to recreate the native control again.
This repeats over and over again when your GotFocus event handler yet again runs for the new native control. This does eventually end when Windows puts a stop to it and doesn't allow any more native windows to be created, it pulls the plug at 10,000 controls after a while. Which generates the "Error creating window handle" exception.
The Enter and Leave events should always be used for focusing events, they only fire if the user actually moved the focus, and don't fire when it happened because of other reasons, like this one. Also notable is that there's no point at all in changing the HideSelection property like you do, the property only has an affect when TextBox doesn't have the focus. The selection is never hidden when it has the focus. So the proper fix here is remove these event handlers and simply set the HideSelection property to True in the Properties window. The default value.
Cant reproduce this works for me:
private void textBox1_Leave(object sender, EventArgs e)
{
((TextBox)sender).HideSelection = false;
}
private void textBox1_Enter(object sender, EventArgs e)
{
((TextBox)sender).HideSelection = true;
}

Setting a "click" method to execute right away

I have a working method
something_Click(object sender, EventArgs e)
{
code
}
...
It of course executes after someone clicks the element. What I need to do is, to execute this method immediately after the element appears on the screen (it is StripStatusLabel). I have tried just to add a call of the method to beginning of the code, but it did nothing.
You can call Button.PerformClick on your button in a Form.Load event handler.
You might also want to consider moving that logic into its own method, and call that method from both the button handler and the Load event, as this will be more clear. (It's obviously code you want triggered on more than just a "button click").
Depending on what framework you are targeting (WPF vs Winforms) you might be able to handle the Load event instead. It triggers when the element appears on screen.
It's not best practice to fire a form event which would normally be originated from teh user. In the sense, you are trying to "fake" the click. Think of the future debugging, or of the colleague who might inherit your project. When inspecting code you would expect something_Click to only be fired when there is a click on "something".
A better solution is to put the "code" part in your snip into a method whose name reflects what it really does.
They you may fire this method in different areas. Fire it at the click, at the load, anywhere.
something_Click(object sender, EventArgs e)
{
DoStuff();
}
OnAppearToScreen()
{
DoStuff();
}
DoStuff()
{
//code that actually does stuff
}
Later on, when you want to check when "the stuff" was done to your object, you can easily tell by code inspection.

Categories

Resources