Todays entry is regarding the System.Windows.Forms.UserControl OnFocus behaviour in conjunction with scrolling. When a control is a child of a parent control, and is slightly scrolled off the top of the parent's top and gains focus, the default behaviour is for the control to jump into full view. This is good. However, if said control represents a list item group (like the kind found in Outlook's message list view) which draws it's own items within it's client area, this is bad. Why? Imagine this (lets call it) ItemGroupControl is 300 pixels high because it contains 10 "items" that it renders within itself. Got that?
Now image that this control is too tall for the parent control to display in it's entirity and suppose that the parent control is set to display scroll bars so that the child ItemGroupControl can be scrolled up or down so that it can be viewed in full. Now, what do you think would happen if the parent control had scrolled it so that only the bottom 100px of it was visible and then you clicked on one of those last visible "items"?
It gains focus (the entire ItemGroupControl; remember, each "item"
listed within it is virtual - not represented by a physical child control but drawn on using the GDI+ API), and when it gains focus, if you'll recall, it gets scrolled into view so that the top is in line with the top of the parent control. This is an attempt to bring the full control into view for the user.
Ouch. So our poor user has tried to select the item in the ItemGroupControl, only to find that the view shoots up and away from the item they were clicking on. So, they scroll it back down and re-select it. Now it works. Why?
Well, when you click on a UserControl on a Form, the control first recieves a WM_SETFOCUS if not already focused message (among others, but I'll ommit them for clarity) followed by a WM_LBUTTONDOWN message. The OnMouseDown method is called, and the default implementation is to first give the control focus if it doesn't have it and then call the base OnMouseDown implementation. Because the control is already in focus so the unfortunate behaviour doesn't occur again. Until the next item within another ItemGroupControl is selected that is.
How do you fix this? If you even care.
Eat the WM_SETFOCUS message sent to the itemgroupcontrol on it gaining focus. This prevents the control from shooting up to have it's top edge visible.
To do this override the WndProc method and test for the 0x07 message (you can get this and many more message definitions from WinUser.h if you download the platform SDK or if you install Visual C++). If it comes up, don't call the base implementation.
It's probably overkill, but it solved my irritating out-of-control-control problem.
Now image that this control is too tall for the parent control to display in it's entirity and suppose that the parent control is set to display scroll bars so that the child ItemGroupControl can be scrolled up or down so that it can be viewed in full. Now, what do you think would happen if the parent control had scrolled it so that only the bottom 100px of it was visible and then you clicked on one of those last visible "items"?
It gains focus (the entire ItemGroupControl; remember, each "item"
listed within it is virtual - not represented by a physical child control but drawn on using the GDI+ API), and when it gains focus, if you'll recall, it gets scrolled into view so that the top is in line with the top of the parent control. This is an attempt to bring the full control into view for the user.
Ouch. So our poor user has tried to select the item in the ItemGroupControl, only to find that the view shoots up and away from the item they were clicking on. So, they scroll it back down and re-select it. Now it works. Why?
Well, when you click on a UserControl on a Form, the control first recieves a WM_SETFOCUS if not already focused message (among others, but I'll ommit them for clarity) followed by a WM_LBUTTONDOWN message. The OnMouseDown method is called, and the default implementation is to first give the control focus if it doesn't have it and then call the base OnMouseDown implementation. Because the control is already in focus so the unfortunate behaviour doesn't occur again. Until the next item within another ItemGroupControl is selected that is.
How do you fix this? If you even care.
Eat the WM_SETFOCUS message sent to the itemgroupcontrol on it gaining focus. This prevents the control from shooting up to have it's top edge visible.
To do this override the WndProc method and test for the 0x07 message (you can get this and many more message definitions from WinUser.h if you download the platform SDK or if you install Visual C++). If it comes up, don't call the base implementation.
It's probably overkill, but it solved my irritating out-of-control-control problem.
Comments