I'm certain this can't be as complex as I'm finding it so far!
I'm trying to render a horizontal list of user controls. There will be a large number of them. So some form of Virtual list would be prefereable.
Each user control will contain an image and be selectable.
In Android/Flex/iOS this is trivial with their List Adapters, List Item Renderers etc... However in Win forms it seems very tricky indeed.
I've looked at ObjectListView setting the view mode to Tile. However there doesn't appear to be a way to render horizontally.
I've tried just populating a flow layout with my user controls. But the memory usage goes through the roof as it's loading images.
You could use FlowLayoutPanel container control and set its WrapContents to false and FlowDirection to LeftToRight (which is default). ...and, probably, AutoScroll to true.
EDIT
As to going out of memory, think of simulating virtualization by handling Scroll event and creating/disposing controls as needed.
Derive your own image control from Control and override OnPaint in order to draw the image yourself. Add a property for the path or name of a picture, but don't store the image itself in the control. Google for custom control c#.
Use a cache for the images. A good data structure for this is a circular buffer. This helps in keeping only a limited number of images in memory.
Related
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.
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.
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).
I'm a bit new to the C#-form app developing and I want know, what's the best way around at making a control that holds a list of horizontal items. In which each of these items are horizontally ruled to it's parent control, contain a thumbnail to the left and a large text block to the right of image and a smaller text block underneath that. So basically this isn't a predefined control I can find in the toolbox. Any ideas?
You could lay this out with Panels in form controls, or with WrapPanel and StackPanel in WPF.
In WindowsForms, I would create a user control that held the correct layout for a single item, then make a list of them at run time.
In WPF I would use a List control, but set the layout template to use WrapPanels and StackPanels.
WPf is the better solution long term if you don't have to coexist with winforms
I was wondering... I have a WinForms System.Windows.Forms.ListView with a bunch of ListViewItems that I'm drawing using the View.List style. Each ListViewItem has a "SmallIcon" that I fetch from the ListView's SmallImageList.
My problem is that the icons are showing too close to the border on the left. I've tried to change the bounds and the ListViewItem's Position property to no avail.
Is there anyway to have some kind of offset to ListViewItems?
The Win32 listview control doesn't have any setting to increase the space between the icon and the label (in any view, not just List). Setting ListViewItem.Position does nothing when the ListView is in List view.
A low-tech solution would be to simply prefix every ListViewItem's Text value with a single space. Slightly ugly, but oh so easy to do.
If you really want to have pixel level control, you will have to owner draw it. As always, if you are doing anything with a .NET ListView, ObjectListView makes owner drawing your items trivial.
As mentioned already, prefixing the text of all your items with a space is a super simple way to add padding. This has a pretty significant drawback, however. Once you do this, you lose the ability to find items in the ListView by simply starting to type their name while the ListView has focus.
Try adding white space to the left of your small images.
If you're using 16x16 images change to 24x16 for example by adding 8 white (or ListView Background color) pixels to the left.
If you are in ListView View LargeIcons then you can postion the text using item.position
A screenshot would be nice for an example to see exactly what you're after.
Funny thing... the Windows Explorer uses the ListView to display files and folders. i usually run my view in Report or Detail mode. i just switched it to List view mode and see the exact problem that you're describing! Yikes. Might be a bug with the Win32 object and that particular view type!
A quick workaround might be to use a Report style for the ListView with a single column or perhaps implement something yourself. The FlowLayoutPanel in .Net would work very nicely as a starting point for a custom list view.
As you are using the View.List style, I suspect you'll either need to implement some custom drawing or consider padding your images. You could also look at overriding the ListView control and manipulating it's bounds by overriding SetBoundsCore or SetClientSizeCore (or similar).
However, if the ListView were set-up for View.Details view, this could be done using the ListViewItem.IndentCount property:
The IndentCount property can be used
only when the View property of the
containing ListView is set to Details,
and the SmallImageList property of the
ListView is set. Source:MSDN
If you set the StateImageList property you can add a space of 16 px before the icons. I think you can adjust this additional space by loading an image with matching width as first entry into the StateImageList. But I have not tested this.
This thread discusses the opposite problem.