Jump to content
Xtreme .Net Talk

Recommended Posts

Posted

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

focustestfailure.zip

Posted

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.

TT

(*_*)

 

There are 10 types of people in this world;

those that understand binary and those that don't.

Posted
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?
Posted
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.
  • *Gurus*
Posted

This is a confirmed bug in Windows Forms. If memory serves, one workaround is to override the WndProc to ignore the WM_SETFOCUS message, but I think that screws up the activecontrol property of the parent.

 

I found the page where I saw it, and it's on the first post. The guy mentions the bug in passing.

 

http://www.windowsforms.com/Forums/ShowPost.aspx?tabIndex=1&tabId=41&PostID=261

MVP, Visual Developer - .NET

 

Now you see why evil will always triumph - because good is dumb.

 

My free .NET Windows Forms Controls and Articles

Posted

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

Posted

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.

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

  • *Gurus*
Posted

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.

MVP, Visual Developer - .NET

 

Now you see why evil will always triumph - because good is dumb.

 

My free .NET Windows Forms Controls and Articles

Posted

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

  • *Gurus*
Posted
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.

MVP, Visual Developer - .NET

 

Now you see why evil will always triumph - because good is dumb.

 

My free .NET Windows Forms Controls and Articles

Posted (edited)

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.

 

/// <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);
}

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

Edited by divil

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...