A few years ago I wrote a graphing application in Java. It stored edges and vertices in a QuadTree which I would loop through whenever I had to draw them.
Today I need to do something similar in WPF, however, I noticed that I have to add elements to a Children list inside a panel in order for it to draw. This is inefficient since now I am effectively storing two versions of the data. Removing elements is also costly compared to using quadtrees.
Are there any better ways of implementing this without having to use the Children List?
In WPF you usually do not use panels directly if you have a data source, instead you use some ItemsControl, bind its ItemsSource, set up the ItemsPanel to whatever panel you want to use, and create an ItemTemplate which defines how the items are to be displayed. (Canvas binding example)
This way of working with panels means you do not have manually take care of the Children collection, if your ItemsSource implements INCC it will stay in synch. I realize that this might be a bit difficult with your trees but maybe you can figure something out.
Related
Alright, so I'm trying to figure out the best way to accomplish this for my rather unique case. I have a fullscreen WPF application where controls are added to a Grid dynamically and the user is given the option to freely move/resize them. The controls can be individually moved or resized anywhere on the parent Grid but I have prevented them from overlapping via their MouseMove events. They also cannot be moved outside of their container. Since the app will be running on machines with different monitor sizes, I need to take that into consideration when making the original layout. I have an initial layout that I would like to use, but it would seem that I'm out of options:
I first tried using rows and columns just to (initially) place the controls that are added. This method places them correctly but becomes a problem when I need to move or resize the elements because the control is already assigned to a particular row and column. I could try to use this method and then remove any rows/columns after placing the controls, but I don't think that would work well.
I've also considered a Viewbox, but that's not practical in my case for fairly obvious reasons (as it merely resizes the controls to fit the screen). I'd prefer not to use this because I would only need it to standardize my initial layout. That's it. I also don't want to mess up any text that will be displayed on the window.
So yeah, this is more of a "best practice" question because any solution I can think of would not look very professional or elegant. Feel free to ask any questions if you need clarification.
Edit: As an additional note, I'd prefer to stick with a Grid as opposed to a Canvas as my container.
Edit 2: Just to be clear, I would not need the specific (inital) layout after the first launch. When the program exits, the layout (Margins, Width & Heights, etc. for each element) is saved to a file to use for the next launch.
I've developed a number of kiosk/interactive applications using WPF. If you are trying to show the element transitions (while moving), then it might be best to use a parent Canvas and bind the Canvas.Left and Canvas.Top properties. You can mimic the grid alignment, using a Canvas, if you put in place mechanisms (e.g. Manipulation/Mouse events, converters) to make sure that the Canvas attached properties adjust to the row/column offsets.
It is not uncommon at all to use the Viewbox to mitigate display differences (and your use case is not "rather unique"). You set the Viewbox to a target resolution (e.g. 1920x1080) and allow the control to fill the available space. The other alternative would be to dynamically apply a content template based on the application window size/ratio.
After looking at multiple options, I've decided to just use a calculation to (sort of) simulate the behavior of rows/columns. Because my application is fullscreen, I can take my SystemParameters.PrimaryScreenWidth and SystemParameters.PrimaryScreenHeight to orient my layout. Using a combination of universal padding (static values) and ratio-based calculations (dynamic values), I can smoothly set my initial layout.
For instance, I'm dividing the width of my monitor by 6 (rounded up to avoid decimals) and using that (minus half the control's width) as the control's Margin.Left property, centering it on a 'column' of sorts.
Honestly, my initial layout is fairly simple right now, so we'll see if this will suffice going forward. Thanks to everyone who contributed to the question, and sorry if I was unclear on what I was asking.
My application (which I am using Visual C# 2008 WinForms for) involves a lot of generated controls. Specifically: grids of buttons, arrays of labels, lists, headings, etc... all populated so that they fit their containers appreciably.
I want users to be able to resize the main form, which obviously would require me to either destroy my generated content, and remake it at the proper size OR I could index through every control, figure out what it is by name and type, and re-size each item individually. I would have to do this while/after the form resizes.
Are there any more intelligent ways of doing this? Dock and Anchor don't quite apply here because I am dealing with items that don't make up 100% of a dimension (for example, grids of buttons).
Hard do give a reasonnable answer without seing just how complex the layout in question is.
But in principle, you should use a layout container such as FlowLayoutPanel or TableLayoutPanel to do the job they were designed to do. If one does not do the job, just nest them.
Docking/anchoring is probably the answer here. You need to anchor your grid to top/bottom/left/right or dock it (same effect, but the grid will fill the parent control).
If this is done right your control(s) will re-size with the rest of the form just as if you created everything in the designer.
I believe something like this would work:
Control.Anchor = AnchorStyles.TopLeft | AnchorStyles.BottomRight;
I have a tree- or menu-like data structure which I want to display in a Panel (specifically a self-made PolarPanel), but all items must be VisualChildren of the same panel.
If I define a HierarchicalDataTemplate, I need to create an ItemsControls and specify it as IsItemsHost, or am I mistaken here? Can I somehow reference the parent's ItemsHost?
<HierarchicalDataTemplate
DataType="{x:Type local:ItemsCollection}"
ItemsSource="{Binding Children}"
>
<!--/* "virtual" ItemsHost referencing the parent's ItemsHost? */-->
</HierarchicalDataTemplate>
(My first workaround was to have a GetAllTheChildren property in the root node which returns all children and all children of the SelectedChild, recursively. This doesn't easily work together with ObservableCollection, because changes have to bubble up to the root to refresh GetAllTheChildren again (which reloads more items than would be neccessary).)
I know I can do it in code-behind by creating and drawing the items' Views into the same parent, but I'm searching for a nice WPF, indirect, automagic way.
EDIT: I am now using an improved workaround, where all ItemControls / Panels are drawn on top of each other. This preserves the hierarchical aspect, which makes this a much better solution than the one mentioned above (with GetAllTheChildren). But it still doesn't answer my question...
This is what it looks like graphically:
Note how the hierarchical elements have to be arranged in the same space.
Is there a clean and simple solution to this?
I must admit that the graphic result is very attractive, even I wouldn't know how to use that kind of control!
To answer to your question, I never tried to share the same panel as host, but I don't think would be possible. That would break the visual tree hierarchy schema. Any visual element cannot have more than a parent.
Assuming that every hierarchy level maps his own ring, I surely bet on the classical way to create such a composite UI. There's no doubt about the convenience of using the HierarchicalTemplates and ItemsControls, even it could be a complex task.
Moreover, I don't understand whether your goal is to have some kind of collapse, such an ordinary tree-view. In this case, avoiding the classical WPF approach would be a nightmare!
My solution is to create a custom panel, being able to arrange their children on a ring.
That's not all. It must be created both a custom Control and an ItemsControl. The first one will represent the ring-slice that has to be rendered, and the other just as the generator.
The control acting as item holder will have two properties: angle and radius. The thickness can be set apart.
The panel, calling MeasureOverride, must take in account the ring nesting, and set both the angle and radius of their children accordingly.
From my viewpoint, the holding control should derive from a ContentControl. That because you need to render the shape of the slice, and that task should be done by this control. Any particular embedded control (text, buttons, etc) can be placed inside as content.
Of course, all that is brainstorming. I'm not guarantee that is the best solution, nor that would work as is.
Hope it helps.
Cheers
I don't know if this is the best way, but what I am trying to do is to create a collection of Shape objects and then bind it to a Canvas, then whenever I update these Shapes, or add new Shapes (rare), the Shape objects on the Canvas will update as well.
Btw the Shape objects are not all the same, some Lines, Rectangles, etc. Not sure if that matters.
What's the best way to do this?
If you look at the question Is it possible to bind a Canvas's Children property in XAML? and further down there is an answer from Ivan which uses an attached property you can bind to and it automatically updates the canvas children. I haven't tried it but looks like it should work. Seems the best solution to me. The other option is to use the ItemsControl with a DataTemplate for each type you want to show - however that seems a bit fiddly.
I have been using WinForms since the first framework introduced and invested a lot of time and effort in it. Now I am attempting to move to WPF and, honestly, it's not so easy.
Now I have a task, I need to implement a simple wizard, each page of which has a aligned to center group of controls. The group contains a set of buttons, four button in a row. Width of the group is constant, height is variable -- it depends on the number of buttons inside.
It's a simple task for WinForms, but I have no idea how to do it using XAML.
I have three questions:
1). Obviously, the buttons inside a group is a WrapPanel which is placed in a Grid's cell. It's simple. But how to calculate height of the WrapPanel not using code behind?
2). Which is recommended way to implement wizard? Data template or some kind of Tab Control? I probably will need to have some transition effects when switching pages.
3). Is it acceptable in WPF world to use binding as a way to repositioning controls?
Thank you in advance!
The WrapPanel will auto-adjust its height based on its contents by default. WPF is a big advancement from WinForms precisely because of the new layout paradigms. No code behind is needed for anything you've mentioned.
As for 2; there are a lot of ways to implement this, depending on how close you adhere to MVVM (if at all); I'd recommend using a styled TabControl at first (you can adjust the style to present visually the steps in the wizard as tabs, without letting the user jump between tabs), as it's easiest. Also, it's possible to bind pretty much everything to the TabControl.
3 is possible, but should be rarely needed. And I mean it.
Now then; a simple example to show you the power of WPF.
You can have in your ViewModel (if you're not familiar with MVVM google it or read any of Josh Smith's articles or book or... wow there's such a wealth of information on it I don't know which to choose) a collection of objects, let's say Step classes, which you can bind to the ItemsSource of the TabControl. The TabControl will automatically create a tab for each of your Step.
Inside your Step class, you can have a collection of items, let's say... um, Collection<Push> (I'm struggling not to use known classes like Action or Button). If the ItemTemplate of the TabControl contains anything that recognizes that collection, as in a ListBox styled internally to use a WrapPanel for its ItemsContainer, you're done: the template will show the list of Pushes in a WrapPanel and that's that.
Now, I probably shouldn't write a full tutorial here anyway, but that should get you started. Read up on DataTemplates, ItemsControl etc. (again, I'm having difficulties picking resources... Dr. WPF has awesome articles on both, but they might be a bit advanced) and you should be good to go. Just remember that there's a good reason why WPF features a lot more fluid layouts than any previous desktop technology, you should become familiar with that.