I understand the syntax but not how really to use it. It's clear in many basic scenarios but as soon as it get's a little bit advanced I start getting a headache.
For example there are many different views but often not clear wich one to use. Also should you use always just one or mix and match. Do you use the view as your itemssource for ItemsControls?
I'm gonna give a scenario. I have items from a database that I need to show info about in a app and also allow to edit and add new. The items form a hierarchy and the models are of different types. So the top level have children and they then have children.
I could show it in a TreeView or some itemscontrol. Problem here is I tend to bind to the children property of the root elements wich returns a List of chilren. Now the children aren't really inside a view, like I can't call editview.addnew() or filter the children straight away. Question is how do I ensure the children are also in a view and their children and so on. Should the model return a view, should I create a seperate view for each children type or even for each parent?
Another thing is if I'm allowing editing should I put the Collections straight into a IEditableCollectionView or wrap it in ICollectionView first (why is that better)?
Is there a good guide to using views that isn't just pure basics?
Related
I've written a custom panel which displays its children docked either vertically or horizontally, separated by moving splitters in between. Since the Grid panel offers much of this functionality out-of-the-box, I just inherited from it.
To create the layout, upon Loaded is fired I do the following:
1) Read how many children it has and create the appropiate number of rows/colums.
2) Position every existing children in the corresponding row/colum.
3) Create, position and add a GridSplitter for every child.
This approach looks and works fine, but it opens the door to a lot of problems:
Since it's added a GridSplitter for each child, there are twice the number of expected children. If someone added 3 elements to it, Children.Count would return 6.
User could insert/remove things at the wrong place.
It just throws an exception when this Grid is used as the ItemsPanel for an ItemsControl, since in this case WPF (not Silverlight) does not allow direct children manipulation.
These 3 cases are the ones I've already tested, but I'm pretty sure a lot more would arise depending on what the user does with it.
As it turns out, this class must be regarded as 'implementation details', so the real question is, what control should I put in front of the user?
It sounds like it should be a Panel, but I can't control the Children property since it's not virtual, and there's also the ItemsControl which I think could be a good candidate, but I really don't know.
I'd much appreciate any kind of advice or some directions to do this the right way.
Thanks in advance.
You see using just grid you leave yourself with an imperative way of adding items only. As in
myCustomGrid1.AddMyItem(***), Grids simply don't have ItemsSource property. ItemsControls do - so if you need support for declarative items sources i.e. myControl.ItemsSource = {Binding ...} you're going to derive your control from ItemsControl. This is not a two liner - making your ItemsPanel Children writable is a big challange - there's no simple way of doing that.
This is all about a small thing overlooked during the Grid's design - splitters shouldn't have been added to Children collection, as Children are visulaizations of your BOs while spliiters are just formatting elements.
Here's what I would do.
Forget about ItemsSource & items altogether - it's aint worht the hassle. The only way to add/remove items to your control will be AddResiazableItem/RemoveResizbleItem. Calls will add items and splitter (for the middle items), extend the number of rows/cols of your grid depeneding on its orientation, set Grid.Row/Grid.Column attached properties for your visual children. You can keep your actual objects internally to support Orientation change.
If at any stage you'll want to bind your control to IEnumerable source - just create an attached behavior, which will iterate through the items and call AddResiazableItem within a loop.
Cheers.
P.S. To moderators - the editor seems to get broken, lads. I cant see the second item.
P.S.S. Got it fixed after a few tries.
Here's the thing:
I have a GetHistoryLog View, its view-model, and its model.
I have a listbox which points to a ObservableCollection<ChangesetEntity>
I have another view specialized on getting versioned items, so it's a VersionedItemView, its view-model, and its model.
Now I want GetHistoryLog View to access specific versioned items within its changesets. Each changeset can have multiple versioned items. What is the best way to "connect" the two view-models?
Here's what I want to do, a Tree-view containing all the changesets and child files, and a single list view containing all the changesets, with an option to click the changeset and view the modified files.
Basically, access a list of items inside a list of items, and at the same time have the option to access a specific index inside this list.
Do I need to create another view-model? What is the best solution without creating havoc in the code?
I hope I was clear enough
Thanks in adv.!
EDIT: Also, is there a way to set the DataContext of a control to some specific item in a list, dynamically?
EDIT: Trying to explain more clearly:
HistoryLogEntryModel -> GetHistoryLogVM -> GetHistoryLogUserControl
VersionedItemLogModel -> GetVersionedItemsLogVM -> GetVersionedItemsLogUserControl
What I want is:
HistoryLogEntryModel + VersionedItemLogModel -> ? -> GetCompleteHistoryLogWithVersionedItemsUserControl
(shorter name, but just for understanding)
Based on your description, I don't think you need anything more. You already have a very hierarchical object graph of your domain which will support the parent child relationships you're describing already.
I think you just need to polish up on your approach to binding and object presentation. Here is a good post from MSDN magazine that can help you with that and it also has advice for dealing with with Hierarchical Data Templates for use with a WPF TreeView.
If you want to do a binding that dynamically changes the DataContext of a control based on the SelectedItem of another control (ListBox for example), you can use the following binding syntax:
DataContext="{Binding ElementName=sourceElementNameHere, Path=SelectedItem,
Mode=OneWay}"
Here is another link to a good MSDN article on binding in WPF.
I've noticed a problem I'm having depending on the Hierarchy of items. When I wan't to represent the items I have no idea how to put it's children in a manageable state.
Let's take a TreeView for example. When the window is loaded up I can take the model and put it into a view and as a source for the tree. However I seem to have no real control over the children. Only thing I can do is point to the children property of object and it shows up all real nice but what if I wan't to edit, sort and/or add a new item there I'm out of luck.
I can't really put the children into the view or a seperate collection to modify as they are only the property of the root element. Only way I can see at the moment is having the property return a ObservableCollection or a CollectionView and that has it's own problems as in no lazy loading for example.
How are you doing this, what is the proper way here and what am I missing?
I've got 2 DataTemplates defined for a Listbox Control. 1 Template is for the UnSelected State and the other one is for the Selected State(showing more detail than the UnSelected State).
I followed the example here:
Link
about how to access the Elements inside the DataTemplates from Code behind.
I get it right, but it only finds and returns an element of the UnSelected DataTemplate. But when i search for an element in the Selected DataTemplate i get a NullReferenceException.
What could i be doing wrong?
Setting keyboard focus might be one reason you need to access the datatemplate elements. MVVM will not solve that issue and the FocusManager doesn't set keyboard focus.
What you are doing wrong?
I would say what you are doing wrong is trying to access elements inside the DataTemplate from code-behind. Naughty, naughty!
All joking aside, 99.9% of the time I see someone trying to access an element inside a DataTemplate from code, it is because their application is poorly designed, with no (or few) bindings and no view model at all. Many beginners tend to store their data directly in UI elements rather than using a view model. I think it is because their minds have been corrupted by experience VB, WinForms, and ASP.NET where it was the "normal" way to do it. There are a thousand reasons to use a view model and bind your data instead of storing them in UI elements. Look up "model view view model" online for more details.
Now to answer your question:
Any given ListBoxItem can only have one DataTemplate at a time. This is because its ContentPresenter has only one ContentTemplate property, and this property cannot have two different values.
Because of this, the visual tree under a ListBoxItem will always be generated from one a specific template, not a combination of several templates. If you change the ItemTemplate of the ListBox or otherwise update ListBoxItem.ContentTemplate, the visual tree produced by the old template will be thrown away and a new one built.
Let me say that again: If you change data templates, the visual tree produced by the old data template will be thrown away and a new visual tree built.
You can have a hundred data templates defined and usable on a given ListBoxItem, but only one at a time can have a visual tree instantiated for it. And these are the only elements that actually exist as part of the visual tree. All other templates exist only as templates - there are no actual elements created for them.
To put it another way: It is meaningless to ask about how to find elements in two different visual trees instantiated by two different templates on the same control, because a single control cannot have two different templates active at the same time.
Hope this clears things up for you.
Final advice: Do read up on MVVM, and stop trying to access elements inside DataTemplates ASAP. However if you think you might be in that 0.1% who actually do have valid reasons to access elements inside templates, write back with your actual reason for wanting to do so and maybe I can provide further guidance.
I am developing a wpf desktop app with strict MVVM pattern.
Current my app is doing following things:
Showing a Treeview with HierarchicalDataTemplate.
User can expand or collapse Nodes.
User can add add new Nodes(Drag n Drop + double click).
Everytime a new Node is added Model is updated and Treeview is recreated based on Model.
Because Treeview is recreated, all nodes are shown as expanded after adding nodes.
I want to show nodes with their previous expanded condition. Is there any way to do this using MVVM ? What I have thought so far is
Model should not contain any data related to how to draw UI ??
VM should just get data from Model and put it in UI(and pass date from UI to Model) ??
Thanks for your thoughts. I may be way far out form rail. But just want to have some wisdom from you guys.
Thanks
PAIJA
If you haven't already, read this great article by Josh Smith: Simplifying the WPF TreeView by Using the ViewModel Pattern
Basically what he suggests there is to include a property called IsExpanded in your VM and bind the TreeView to it correctly so that the expanded/collapsed status is entirely controlled by the programmer.
One solution what I think could be is to stop the recreation of the tree, just update the model and only add nodeitems to the current node where you are dropping them. Just refresh the collections in model and dont refresh the tree. Let us know if this does't suits your architect.
Thanks,
Jagdev Josan
The view model can contain view related information, that is what it is for. It is a bridge between pure business and pure view. My view models usually expose a few business properties of an object and add a few related view properties. If all you need is business properties, then bind straight to the business layer. Its only when you need to do something like your situation here that you need a view model.
If you want to completely recreate the tree (which sounds crazy) you can store the expanded state of the nodes in your view model and bind them to the tree view items using an ItemsContainerStyle. that way when you recreate your tree view your previously expanded nodes will still be expanded.
So your wrapped business objects will contain an extra property IsExpanded that you can use to restore your tree view state.
P.s. did i mention its a bit over the top to recreate the tree view?