C#: How to draw very large controls in a scrollable, zoomable panel? - c#

Imagine a bar graph with horizontal bars that may be very wide. I have a Panel on a Form where I want to display these bars and scroll and zoom them. The Form, and thus the Panel, can be resized. The bars are dynamically created from a database. Each time the user zooms in or out, all bars have to be created anew to adjust their sizes on the Panel.
I use Label controls to create these bars, but the problem applies to all other controls as well: If I zoom in far enough, my bars will eventually exceed the magic 16 bit border of control sizes (>65536 pixels). This makes it impossible to simply create all the controls on the panel at start and let the panel handle the scrolling.
My idea: Clear the Panel of all bar controls and create only the ones that are visible in the current view window, according to the current position of the scroll bars and the zoom level. The bars exceeding far from the visible view will be cut short just outside the Panel, so their maximum size is limited by the Panel size.
My questions:
At which Panel event(s) should this clear/create process take place best?
There could be thousands of controls, so it should be as seldom as possible.
Is there a better way to handle this? Maybe I got it all wrong from the start.
This problem arises not only with huge controls but also when smaller controls are very far apart (>65536 pixels) on a Panel, so I think a good solution may be helpful for many projects.

I wouldn't like to have to create / destroy controls, or hide / resize controls just for their click events. It's quite easy to create a UserControl and override the OnPaint method to draw the bars, and override the OnClick or OnMouseXxx events.
Since you already know the positions of the bars in "virtual space", it's easy to map the location of the mouse cursor to a bar (or a click outside a bar).

I know you said winforms is Mandatory, but I really think you should look into the wpf viewbox. You can host a wpf element in winforms. So everything else can be forms related, and you have a panel that hosts and displays your controls. I could write up a quick example that might demonstrate this for you, but if you have no intent of going this way I really don't want to waste my time.

You could create a metafile (vector graphics), show that in an image control, and manually determine which logical element is clicked.

Related

Trouble using Windows forms to design GUI

I'm trying to write a simple GUI that renders a number of images using the Graphics' object primitives. What I want to have is a series of areas that I can paint to in isolation of the other areas, so that each painting "canvas" has it's own origin within the global coordinate frame of the top-level form.
So far I have tried adding several panels to a FlowLayoutPanel. However, they seem to be getting placed one on top of the other, as only one onPaint method is being called. I can override the Form's onPaint to invalidate the other panels, which are then painted, but not displayed.
Besides setting the sizes, and initialising the FlowLayoutPanel, is there something I'm missing? Is there a better way of doing this?
Code: http://pastebin.com/30Uf9AGF
based on the names of your classes, it looks like you are designing a game ... maybe you want to take a look at Microsofts XNA framework?
however, the problem with the code you provided is, your layoutPanel is not sized correctly, therefore its child-controls are not visible on the main form... since painting is only done for visible items ... there is no painting for most of your FloorDrawPanels ...
try changing the size of your layoutPanel or setting its dock mode to fill
You don't set the size of the FlowLayoutPanel. It will default to 200 x 100 with a Margin of 3. You fill it with controls that are 100 x 100. Given the margin, only one of those controls can ever be visible at the same time. It is therefore no surprise that you only ever get one paint event, Windows only asks visible controls to paint themselves.
Not quite sure what was intended, start by making the FLP bigger. And set its AutoScroll property to true so that the user can scroll the other controls into view. Using the designer would have been a quick way to find this out btw.

How to center selected grid row/column at the center of the window?

I just wasted my entire evening on something which I thought would be very simple but it seems WPF and Google are letting me down completely.
I need a grid, 6x6 of which I fill every row and column with a custom control. I want to be able to navigate through this grid via the keyboard (I can get those events, no problem) but I cannot seem to find how I can always have the selected grid row/column in the center of my window.
I found some carousel alike implementations, but most of them only work in a single direction and I want two way navigation, yet none seem to support this nor can I extend them to do this.
I essentially want to create a PSP alike grid navigation.
One easy way is to do this:
Create a scrollable form.
Add a 6x6 grid of child controls.
In the GotFocus (or similar) event for all the controls, set the parent form scroll offset to an appropriate position to centre the child.
This is pretty straight-forward thing to implement, with a little bit of maths to work out how to centre the x,y position of a control by setting the scroll offsets (it can be tricky/confusing, but as long as you understand the coordinate systems used for scrolling, not too bad)
Or, another approach that avoids scrolling via the windows APIs and using custom controls:
Create a form
Override OnPaint to draw your grid of 6x6 "controls" as simple graphical shapes or bitmap images centred on the selected "control".
Handle keyboard (KeyDown/Up) and mouse handling (MouseDown/Up) events to make the 36 areas of the graphic respond to user inputs in the way you desire. You'll have to track the selected item and force the window to redraw its graphics to show the new state. Enable double buffering to stop it flickering.
The first approach gives you a lot of windows-based handling for free (tabbing between controls, remembering where the input focus is, and directing events to separate classes for each "control", for example). The second approach strips away all this "help" but gives you complete control over everything, which can often help avoid unintended behaviours (e.g. it won't move the input focus when the user presses Tab unless you specifically write the code to make it do that).

Auto scrolling control (WinAPI)?

In C# (.Net) you can create a panel and set autoscroll to true. you can then add controls into it, including beyond it's size, then it will scroll.
I was wondering if when using the real WinAPI in c++ (not .net) how one could acheive the same result.
Must I keep track of the controls inside and move them all when I scroll or is there an easier way like in C#.
Thanks
For an edit control (textbox), you can supply ES_AUTOVSCROLL when creating it via CreateWindow or CreateWindowEx. For adding a scrollbar for multiple controls in a window, I believe you have to do it manually. Write a function to sum the vertical height of all the child controls + spacing between them and if it's smaller than the window, add a vertical scroll bar, if it's larger, remove the vertical scroll bar (if it exists). Be sure to call this after adding/removing child controls and on window resize.

C#: Anchored Panel won't resize properly

I've a strange problem regarding auto sizing of a panel in a user control. This panel is anchored to all 4 sides of the user control.
But the anchoring not always works as it should:
If the user control is resized by resizing the form, the right and bottom anchors don't work. But if the control is resized by using a splitter which is in the form, it works correctly.
All other controls which are also affected by resizing work correctly, just this panel won't do.
I can't see the difference between resizing through a splitter and through a form.
Setting the size manually in the OnResize of the UserControl only works if the resizing is done by moving the splitter. If the resizing occurs by resizing the form, the heigth and the width of the panel won't change to the new values.
Edit: To make it more clear: The user control is added to a TabPage and set to Dock=DockType.Fill.
Edit2: To make it more complicated: This is the hierarchy the control is used:
The Form contains a splitter with 2 controls (A and B). (B) contains a user control (C) with a splitContainer. The split container->panel1 in (C) contains a tabcontrol (D). This tabcontrol (D) contains a user control (E) (in a tabpage) . This user control (E) has a splitcontainer (F) in which another usercontrol with a tabControl (G) is placed. This tabcontrol (G) has tabpages, and in this pages is the described user control (H) placed.
If I place the control (H) directly to the panel in the splitcontainer (C) everything works fine. Since all controls which are added to a splitcontainer or a tabpage are set to docktype=fill, I cannot see why the last control has this problems.
Is there any limitation in the Windows Forms framework which limits the amount of child controls?
Edit: I have uploaded a TestApplication with which you can reproduce this issue.
http://rapidshare.de/files/49092516/TestApplication.zip.html
It occurs only under Vista. Win XP and 7 work properly...
If you resize the form, the textBox with the "..." button won't resize, but if you resize via the vertical splitContainer, it works.
There is a bug with deep nesting where the kernel runs out of stack space causing resizing, docking and anchoring to fail silently.
See https://web.archive.org/web/20140818095718/http://support.microsoft.com/kb/953934
You have talked about both "anchor" and "dock", which can serve similar purposes but act differently. Your problem may be that you are trying to do both, and this is interfering with the behavior you want. If not, perhaps try switching from the one to the other.
It sounds like you've double checked the ansectery of the control, but did you examine it with: View->Other Windows->Document Outline (a very handy tool I only recently discovered)?
Finally, while it doesn't directly solve your problem, it sounds like you have a very complicated UI, and your users may benefit if you were to spend some time to consider if you could simplify the UI. Of course, I don't know your users or the purpose of the app, and what you have may be the best, but it sounds like it might be time to ask this question.
Good luck.
The problem is not the depth of nesting; controls can be nested arbitrarily deep. Anchoring and Docking generally do not play nicely with each other. I usually favor docking over anchoring, but, whichever solution you choose, you should probably avoid mixing them.

How to create collapsible panels as custom controls in Winforms?

The closest I can think of using is the GroupBox control which has a custom drawing at the top identifying the panel.
Is it doable? I have done independent custom controls like buttons, sliders, color pickers, but not totally sure if this sort of drawing can be done on a GroupBox?
I would need the grouping of a GroupBox or a TabPanel.
Are there any alternatives on the net that I can use?
Just exploring the alternatives before I start doing it.
A technique we used was to create multiple panels docked to the top of a frame and set their height to zero. Then when we wanted to display a panel we set its height correctly. Winforms automatically reflows other panels to accommodate the new one.
You can also create a recurring timer to trigger every few hundred milliseconds that increases the panel's height by a few pixels until it reaches full size. This creates a nice animation of the panel expanding into place.
See how others have done this. Code Project's got a bunch. Try searching there.
I used this one a while ago and liked it:
Windows XP style Collapsible Panel Bar
Why not just create a composite control? Something to the effect of.. 2 panels or a label and a panel or whatever suits your needs. Drawing it wouldn't be all that difficult but seems overly complicated for your desired functionality. Am I completely missing the mark?

Categories