Desperate; focus repositions if form has usercontrol

bwells

Regular
Joined
Feb 25, 2003
Messages
84
I posted a rather confused request earlier and I have greatly simplified my problem and I have attached a winzip file with a project that demonstrate the problem.

I have created a MDI parent form. I have also created two different form classes to be MDI child forms. One of the form classes has a Label object inside. The other form has a UserControl inside.

For my example project, I have created two menu items on the File Menu to aid in reproducing the problem.

One menu item lets you create an instance of the Form with a label and the other menu item lets you create instances of the form with the UserControl.

The first step is to create two of the child forms. Test one is to create two of the forms containing the Label, and test two is to create two of the forms containing the UserControl.

For both tests, after the MDI child forms have been created, shrink them down vertically so scroll bars appear. Then drag the scrollbars down to the bottom of the form.

For both test, you will select one child form, and then select another child form, in effect, changing focus between the two forms.

For test one with the Label case, nothing happens and it works as I would expect it to work.

For test two, when you click on one child, then another, the contained UserControl jumps and the scroll bar moves to reposition the contents of the form.

I dont want the contents of the child form to be repositioned when you change focus to/from a child. I want test two to act the same as test one.

Having this behavior really breaks my app; I cannot have windows automatically moving contents after a user has carefully positioned the windows and the scroll bars where they want them.

How can I keep the contents of a form with a UserControl in the same place even if the user moves around between different MDI child windows?

Any help would be greatly appreciated!

Bryan
 

Attachments

This isn't ideal but it works. In each form, create a variable of type Point. Then, in the form's Deactivate event, set your point variable to Me.AutoScrollPosition. In the forms Activate event, set Me.AutoScrollPosition to your point variable.

This works, but with a very slight flicker as the form scrolls and then comes back again.

There's probably a better way (Divil?) but I'm new to this.
 
I tried your idea, but my Activate event only gets called once when I open the form, and the deactivated event handler never gets called. Perhaps there is another way to watch the focus for focus change events?
 
I tried the Enter/Leave events, but it gets messy because the enter event gets called frist, then the leave event, then the enter event gets called again so my cached last scroll position gets set back to zero.
 
Hi Divil

I read the post you pointed me to, thanks. Would it be possible for you to post a bit of code to show me how I can either override teh WndProc and ingore the WM_SETFOCUS message, or give me some pointers to how the person on the web forms site found a work around?

He said: setting the scroll state to manual during the WM_SETFOCUS handler)-- though I still had to use reflection to access the private SetScrollState method.

I am still pretty new to this and I dont really understand how to do what he said was his work around.

I would appreciate any help you can provide. I wonder about finding a consultant who can solve this for me if push comes to shove. Or perhaps this is fixed in the new version of .NET?

thanks
Bryan
 
I don't know whether it's fixed or not. I'll try to knock up an example of overriding WndProc tonight. If I forget, bump this post up to remind me.
 
Hi

I received the following reply on the .NET Forms Forum about the bug I have been trying to solve. But when I tried his sugestion, it did not yet solve the bug I am trying to fix. Does anyone have any other ideas or perhaps an idea for why his suggestion does not solve the problem?

Michale offered the following solution:

Ok, I tried this with a Panel instead of a UserControl and it didn't repro. Then I scratched my head and wondered why. A quick scan through our code base helped my understand.

UserControl inherits from ContainerControl, which inherits from ScrollableControl. Panel inherits directly from ScrollableControl. It's the ContainerControl level thats causing this. ContainerControl overrides AdjustFormScrollbars and scrolls the control into view.

So the fix for this is easy enough. Just override AdjustFormScrollbars:


protected override void AdjustFormScrollbars(bool displayScrollbars) {

}



Of course this could be bad as you aren't calling base.AdjustFormScrollbars() and any of the work ScrollabelControl was doing in AdjustFormScrollbars won't be performed any longer. If you cannot live with this limitation, and you are in full trust, you can cast your UserControl to a ScrollableControl and use reflection to invoke AdjustFormScrollbars.
 
You sent me a PM requesting help and I replied - did you not get it? I managed to prevent that bug from happening in the example you posted.
 
Hi Divil

I am sorry I did not get your reply. I am not sure why.

Can you please resend or repost your solution?

thanks
Bryan
 
Sure:

Ok, I downloaded your project and had a little play and I got something working with minimal changes to the code you just sent me. In the overridden WndProc method, in the switch statement you're checking for WM_SETFOCUS and WM_KILLFOCUS and responding to them.

I think the guy on the forum actually meant to ignore those messages as well as set the scroll state, so I replaced your two break statements with return statements so the base method is never called. It worked.

Let me know if this works for you.
 
Okay, I messed around some more and I found that if I moved the WinProc override to the user control, then my example
started working like you said. I orginally had the override on the containing form, but with the override on the user control, it works correctly.

Are there drawbacks with overriding this method and
NOT calling the base class for the focus in/out events?

thanks
Bryan
 
The only drawback (that I know of) is that the ActiveControl property of the parent may not actually reflect the control that has the focus. I don't see this as a major drawback, in fact I don't think I've ever used that property before.
 
I got this message from the guy on the Forms forum about how he solved the focus problem. But when I tried this, it did not completely solve the focus problem I was experiencing, so I am using the original idea of ignoring the focus in/out events.


------------------------------

Sure, here's my C# code; I put this override into all of my forms and controls now.

C#:
/// <summary>
/// Override this virtual method and stop DOTNET getting hold of
/// the WM_SETFOCUS notification. This prevents DOTNET resetting the
/// AutoScrollPosition when this control receievs the focus.
/// </summary>
/// <param name="m">The WIN32  message</param>
protected override void WndProc(ref Message m)
{
	switch (m.Msg)
	{
		case 0x0007:
// Trap WM_SETFOCUS message
//Ok, this is annoying.  Simply ignoring the focus event causes problems with
//the active control and command routing, so instead we'll supress the scroll
//state bits that control autoscrolling.  Just to make life more fun and
//enjoyable, Microsoft made the damned thing Private, so we'll use
//reflection to bypass the protection
	if(Parent is ScrollableControl)
	{
	Type scrollableControlType=typeof(ScrollableControl);
	System.Reflection.MethodInfo sssInfo=scrollableControlType.GetMethod("SetScrollState",
					System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
	object [] clearautoparms={1,false};
	object [] setautoparms={1,true};
	sssInfo.Invoke(Parent,System.Reflection.BindingFlags.NonPublic,null,clearautoparms,null);
	base.WndProc(ref m);
	sssInfo.Invoke(Parent,System.Reflection.BindingFlags.NonPublic,null,setautoparms,null);
	}
return;
}
// Pass all other message to base
base.WndProc(ref m);
}
------------------------------------
 
Last edited by a moderator:
Back
Top