TreeView using Hierarchical Data Templates binding to different collections - wpf

I'm using a TreeView with a Hierarchical Data Template to bind to a View Model hierarchy, my problem is i have multiple child collections of different types (same base class though). Seems relatively simple to use the template to bind one of the collections but i'm struggling to work out how to do both.
class ParentViewModel
{
List<FooViewModel> FooCollection {get; set;}
List<BarViewModel> BarCollection {get; set;}
}
This is kind of what i'm trying to achieve, but wondered if there's a simple way to do it:
http://www.codeplex.com/ComplexDataTemplates

There's no way that I know of. When I've needed to do this I've added another property to the ViewModel, called say, Children, which aggregates the two collections into one:
public IEnumerable<ViewModel> Children
{
get
{
foreach (FooViewModel foo in FooCollection) yield return foo;
foreach (BarViewModel bar in FooCollection) yield return bar;
}
}

If your different collections are only on the root node, you can just add multiple treeviews, and bind each root to a different collection.
Alternativeley you can just add multiple treeviews within the HierarchicalDataTemplate and specify indiviual bindings for that each treeview.
I'm not sure if that will help your specific situation, but personally, I am using the treeview for Menu system, that binds to completeley different menu options.

Related

Two ListViews with different filters on the same data set

I have a ViewModel with some ObservableCollection _questions, which is loaded from DB when VM instance created. Also this list is used to collect data to save back to DB.
This Vm is used for a View1 and it displays the list in ListView with filtering by a property using CollectionViewSource.GetDefaultView(_questions).Filter = ...
Now I need to create View2 which will display same list but without filtering.
I can't bind it to the same ObservableCollection _questions because it has filter defined on CollectionViewSource, but I need to use it to keep SaveToDb code same.
Is it possible to have different filtering on the same data source for two different ListViews?
I have never enjoyed using CollectionViewSource. I would instead filter using a new property in my ViewModel that filters using Linq:
public IEnumerable<MyType> FilteredItems()
{
get
{
return MyCollection.Where(x => x.MyProperty == SomeValue).ToArray;
}
}
I would then bind ItemsSource to this property and use INotifyPropertyChanged event to notify UI of changes to the collection.
Its hard to tell if this fits your scenario well as there is not much information provided on what you need to achieve.

Sorting on Templated Column with converters

My datamodel is like this:
public class ModelA
{
public int ModelId{get;set;}
}
public class ModelB
{
public IEnumerable<ModelA> ChildObjects{get;set;}
}
Now in the Xaml, am using a DataGrid with the ItemSource as List(), and have a template column which binds to ChildObjects with a converter doing the job of getting the first element from ChildObjects and returning the value as that object's ModelId. Now all works fine till now. The issue is when I do sorting on this templated column.
I know one workaround is to have an extra property in ModelB which does the job of what converter is doing and make the sortmemberpath in xaml as that new property name, but that is not what I want as its against the model.
Is there any other perfect way to handle this scenario, as the SortMemberPath can't be made as expression as its just a contant.
You've tagged this MVVM, which I assume means your models are actually view models (or are at least wrapped by view models). That being the case, why wouldn't you add the extra property? After all, it's there to support the view. Your view needs the extra property, so your view model should provide it.

Caliburn Micro - ActivateItem using Container

I was going through the Caliburn Micro documenation here. Simultaneously, I was trying to put up some rough code for experiment. I am a little confused about how to activate item using a container and how to pass an object to the ViewModel that we are activating.
Lets consider a master/detail scenario. The master contains a list (say datagrid) and the details contain specific row from the master for update(say tab item inside tab control). In the documentation (for ease of understanding), I believe the detail ViewModel was directly instantiated using code like this
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive {
int count = 1;
public void OpenTab() {
ActivateItem(new TabViewModel {
DisplayName = "Tab " + count++
});
}
}
So, to apply the above fundamental concept in real world app, we need to instantiate the DetailViewModel (TabViewModel above) using container(say MEF). The challenge then is to know whether the particular DetailViewModel is already opened in the TAB Control. The immediate crude thing that came to my mind was maintaining a List of the Opened Tabs (DetailViewModels). But then we are again referencing DetailViewModel in the MasterViewModel defeating the purpose. Is there any options available to solve this issue.
The second thing that is troubling me is how to pass the Objects from MasterViewModel (Selected Detail Item) to the DetailViewModel. If we use the EventAggregator here then each of the opened DetailViewModels will receive the event which I am not sure how to handle.
If anyone can throw some light on the above two issues, I would be grateful
Update:
The Master is Conductor like this
public class MainViewModel : Conductor<IScreen>.Collection.OneActive, IShell {
....
}
And the detail is defined like this
public class TabViewModel : Screen {
....
}
Both are in the same Window.
I'm not sure exactly what the issue is. In your conductor of many, you have an Items collection provided by Caliburn.Micro. When you come to display a detail view, you can check this collection for the existence of that detail view (using the primary key which you have from the master view).
If the item is already in the Items collection then just activate it (using the ActivateItem method). If the item isn't in the collection, then instantiate it (presumably using a factory if you're using MEF), and add it to the Items collection, and then activate it.

Prism: Share ViewModel's property in parent ViewModel

I'm developing WPF Prism application using Unity container. The issue is: I have a ListBox, each element has it's own ViewModel. In that element I need to select a location from a list of locations. List of locations is the same for all elements. How could I share this list in the parent ViewModel?
On the internet I googled that I may:
Use RegionContext. But
it's not right way (RegionContext could serve only one object, but I have not only locations).
Use SharedService. But, by my opinion, this way is more suitable
for real-time data changing.
Is there the right way? Best practice
If your list is always going to be the same, I usually use a Static class
public static class Lists
{
public static List<Location> Locations {get; set;}
static Lists()
{
Lists = DAL.GetLocations();
}
}
Then in my XAML
<ListBox ItemsSource="{Binding Source={x:Static local:Lists.Locations}}"
SelectedItem="{Binding CurrentLocation}" />
Besides Rachels solution you can create a new view model for the list and insert an instance of this view model into your IoC container. Every view model that resolves this list view model via the container will then get a reference to this single instance.

WPF/MVVM: Delegating a domain Model collection to a ViewModel

A domain model collection (normally a List or IEnumerable) is delegated to a ViewModel.
Thats means my CustomerViewModel has a order collection of type List or IEnumerable.
No change in the list is recognized by the bound control. But with ObservableCollection it is.
This is a problem in the MVVM design pattern.
How do you cope with it?
UPDATE: Sample of how I do it:
public class SchoolclassViewModel : ViewModelBase
{
private Schoolclass _schoolclass;
private ObservableCollection<PupilViewModel> _pupils = new ObservableCollection<PupilViewModel>();
public SchoolclassViewModel(Schoolclass schoolclass)
{
_schoolclass = schoolclass;
_schoolclass.Pupils = new List<Pupil>();
foreach (var p in schoolclass.Pupils)
Pupils.Add(new PupilViewModel(p));
}
public Schoolclass GetSchoolclass
{
get { return _schoolclass; }
}
public int ID { get; set; }
public string SchoolclassName
{
get { return _schoolclass.SchoolclassName;}
set
{
if(_schoolclass.SchoolclassName != value)
{
_schoolclass.SchoolclassName = value;
this.RaisePropertyChanged("SchoolclassName");
}
}
}
public ObservableCollection<PupilViewModel> Pupils
{
get{ return _pupils;}
set
{
_pupils = value;
this.RaisePropertyChanged("Pupils");
}
}
}
I deal with this by not doing it the way you describe.
If I need to present a Foo object and its related Bar objects in the view, the FooViewModel will generally implement a Bars property of type ObservableCollection<BarViewModel>.
Note that this is irrespective of whether or not the underlying Foo class has a Bars property of type IEnumerable<Bar>. The Foo class might not. The application might not even need to be able to iterate over all of the Bar objects for a Foo, except in the UI.
Edit
When my view is a simple representation of the application's object model, I pretty much do things as you do in your sample. The code in my constructor is generally a bit more compact:
_Bars = new ObservableCollection<BarViewModel>(
_Foo.Bars.Select(x => new BarViewModel(x)));
but it's essentially the same thing.
But this assumes that Foo actually exposes a Bars property. It might not. Or maybe only some Bar objects should appear in the view. Or maybe they should appear intermingled with other objects, and the FooViewModel should expose a CompositeCollection of some kind.
The point I'm making is that the view model is a model of the view. This doesn't necessarily have a direct correspondence to the underlying object model.
To pick a simple example: My program may give the user a way of putting items into five different categories by dragging and dropping them into five different ListBox controls. Ultimately, doing this sets a Category property on the Item object. My view model is going to have a collection of CategoryViewModel objects, each with a property of type ObservableCollection<ItemViewModel>, so that dragging items back and forth between collections is simple to implement.
The thing is, there may not even be a Category class in the application's object model, let alone a collection of Category objects. Item.Category might just be a property of type string. The CategoryViewModel isn't mirroring the application's object model. It only exists to support the view in the UI.
Ok, I'll go ahead and add my thoughts as an answer instead of in the comments. :)
I think the bottom line is that this is just the reality of the way WPF and databinding work. In order for two-way databinding to operate, collections need a means of notifying controls that are bound to them, and the standard lists and collections used in most domain objects don't/won't/shouldn't support this. As I mentioned in a comment, being required to implement INotifyPropertyChanged for non-collection properties is another requirement that may not be met by a standard domain object.
Domain objects are not intended to to be viewmodels, and for this reason you may find that you need to map back and forth between the two types of objects. This is not dissimilar to having to map back and forth between domain objects and data access objects. Each type of object has a different function in the system, and each should be specifically designed to support their own role in the system.
All that said, Agies's idea of using AOP to automatically generate proxy classes is very interesting, and something I intend to look into.
What I do is instead of using ObservableCollection in my domain model is use my own collection type that implements the INotifyCollectionChanged interface amongst other useful standard and custom interfaces. My way of thinking is that much like Rockford Lhotka suggests in his book that change notification is useful in to more than just a presentation layer since other business objects within the domain layer often need some sort of notification when state changes in another object.
With this methodology you could create your own collection type that still has the benefits of change notification and as well as what ever custom behaviors you need. The base class for your collection could be implemented as purely infrastructure code and then a subclass could be created that could contain business logic using the subtype layering techinque used in this book. So in the end you could have a collection that can wrap types of IEnumerable<> and provide the change notification stuff your looking for as well for both your domain model and presentation code.

Resources