Caliburn Micro - ActivateItem using Container - wpf

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.

Related

WPF MVVM: an issue of organising ViewModels

what I think I face is a problem of design - one I assume has been solved many times by many people since it seems like it would be very common.
I'm using WPF with XAML and a simple MVVM approach, for the record.
My intention is to create a TreeView, using the MVVM design pattern in WPF.
I have a data model which contains two classes: Scene and Character. Each Scene contains many Characters.
I have created a CharacterViewModel, which is fairly simple (and works fine). Naturally this wraps around the existing Character class.
The Scene class is where I'm getting confused. As I understand it, the SceneViewModel should wrap around the Scene class, just as the CharacterViewModel did for the Character class. But the difference is that Scene contains a list of Characters and thus adds exta complications.
The two options seem to be as follows:
Option 1: Scene contains List of Character and so SceneViewModel also will have that as part of it.
Option 2: Scene contains List of CharacterViewModel and so SceneViewModel will also have that as part of it.
I'm not sure which one to go for to be honest. I suspect it's the second (and this tutorial seems to agree (example 6 is the heading for the section I'm referring to). The first option seems like it would make things really weird (and also why created the CharacterViewModel at all?) but the second seems strange because it seems to muddy the waters regarding what should be in the model part of the program and what should be in the view model part of the program.
I hope I've explained my problem and I also hope someone can offer some help.
Thanks.
Let me first address this statement:
...the SceneViewModel should wrap around the Scene class, just as the CharacterViewModel did for the Character class.
This isn't exactly true. View model should be created for each view. There may be a one-to-one with your model classes, but that isn't a strict part of the MVVM idea. One view may need to present data from multiple "root" model elements (model elements that don't have an explicit relationship like the parent-child relationship in your application), or you may need to have multiple views for a single model element. And to elaborate further, each view model should ideally be isolated from the view technology as much as possible (i.e. a single view model is sufficient to create a WinForms view or a WPF view or an HTML view, etc).
For example, you may have a view that displays data from your Scene class. That view may also display some data for each Character in your Scene. The user may be able to click on a Character and open a view just for that Character (e.g. a popup). In this case, there may be separate view models to represent the Character in the root view and the popup. I tend to name my view model classes according to the root of the view. For an application like yours, I would have something like SceneViewModel and SceneCharacterViewModel (or SceneViewModel_Character, or CharacterInSceneViewModel -- any of these names conveys that the class is for representing a Character in a view for a Scene). This would differentiate that view model from the popup view (which would be Character-centric and would be named something like CharacterViewModel (or even CharacterDialogViewModel or CharacterPopupViewModel or CharacterEditorViewModel).
Keeping collections in sync between the model and view model is annoying but often necessary. Not always necessary, mind you -- there will be cases in which you'll find there are no additional view-model features that you need to add to a model, so it's perfectly acceptable for the view to reference the model directly in this case.
An example of keeping a model collection and view model collection in sync: Suppose your root SceneView has a button for each Character. That button will display a popup for the Character. Suppose further that the Character popup doesn't have a similar button because then it would allow the popup to open another popup (etc). You may want to use an ICommand implementation so that you can just bind the button to the command. It's definitely not appropriate for the ICommand instance to be in the model (even though the command may call a public method on the model). The appropriate place for this would be in the view model for the Character in the Scene view (not the view model for the Character in the popup). For every Character in the model, you would need to create a view model that references the Character and stores additional view-model stuff (the ICommand object).
This means that, as Characters are added/removed from the Scene, you need to create view models specifically for those Characters within the Scene view model. I would typically do this:
At construction time (or whatever time the view model initially receives the model), create a view model for each child object. Put those view models into a public property with a type of something like ReadOnlyCollection<SceneCharacterViewModel>. Your view will bind to that collection.
As child objects are added to the model (either internally or through a public method on the model), the model should notify the view model in an appropriate way. Since the model shouldn't have a direct reference to the view model (not even through an interface -- a model should be completely functional even in a non-UI context, in which there is no view model), the most appropriate way is to use events. You can do this a couple of ways:
Expose events from your model like CharacterAdded, CharacterRemoved or even CharactersUpdated (the last of these would be able to communicate either an add or a remove using a single event)
ObservableCollections (or ReadOnlyObservableCollections), which are most commonly used in view models, can also be used in models, in which case all the events are already available to you. The downside to this is that processing the events off of these collection types isn't the easiest thing.
A third option that is totally different: If your view model or command instance is directly invoking a method like sceneModel.AddCharacter(newCharacterModel), then you can just update your view model immediately after this line without needing any events. I often find myself starting this way because it's simple, but I almost always end up using one of the previous two techniques instead, as those techniques allow the model to notify the view model even in cases where the update is happening internally (e.g., in response to a timed event or asynchronous operation that is controlled by the model).
All of that being said, here's what a "pure" MVVM architecture would look like for your application. Purity can come at the expense of simplicity, so sometimes it's better to take some shortcuts here and there. One common shortcut: In WPF, it's often easier just to put all of your child widget content in the ItemTemplate of the ItemsControl that is being used to diplay your children, rather than creating a separate UserControl for the children.
I guess from your explanation Scene is the Model and SceneViewModel would wrap additional view related functionality to your model in the view model. Same applies for CharacterViewModel.
Whatever your view needs to display you would have a SceneViewModel with a list of CharacterViewodel or vice versa. Or like mentioned your could build up a tree structure with your ViewModels.
My personal view of things is, it is important to stay in the ViewModel universe. So when your construct a view model you would inject your model via a service and build up your ViewModel and only have lists with view models. You would need to do some mapping but there are useful frameworks like automapper, etc already available. But remember there are no hard rules with MVVM.
I didn't understand your choices, However, I think you just need one View Model and it should contain an ObservableCollection of the Scenes. I name it SceneViewModel:
public class SceneViewModel
{
public SceneViewModel()
{
Scene m1 = new Scene() { Name = "Scene1", Characters = new ObservableCollection<Character>() { new Character() { Name="C1" }, new Character() { Name = "C2" } } };
Scene m2 = new Scene() { Name = "Scene2", Characters = new ObservableCollection<Character>() { new Character() { Name = "D1" }, new Character() { Name = "D2" } } };
Scene m3 = new Scene() { Name = "Scene3", Characters = new ObservableCollection<Character>() { new Character() { Name = "R1" }, new Character() { Name = "R2" } } };
_scenes = new ObservableCollection<Scene>() { m1, m2, m3 };
}
ObservableCollection<Scene> _scenes;
public ObservableCollection<Scene> Scenes { get { return _scenes; } set { _scenes = value; } }
}
Scene will have an ObservableCollection of Characters
public class Scene : INotifyPropertyChanged
{
ObservableCollection<Character> _characters;
public ObservableCollection<Character> Characters { get { return _characters; } set { _characters = value; RaisePropertyChanged("Characters"); } }
string _name;
public string Name { get { return _name; } set { _name = value; RaisePropertyChanged("Name"); } }
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}
}
and at last, Character:
public class Character : INotifyPropertyChanged
{
string _name;
public string Name { get { return _name; } set { _name = value; RaisePropertyChanged("Name"); } }
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}
}
View
<TreeView DataContext="{Binding}" ItemsSource="{Binding Scenes}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Characters}">
<TextBlock Text="{Binding Path=Name}"></TextBlock>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"></TextBlock>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
public MainWindow()
{
InitializeComponent();
DataContext = new SceneViewModel();
}

What should compose View with ViewModel and show result?

I have problem of thinking ideal solution for creating and showing window in WPF MVVM application. Some part of application needs to show some window with some data. I create VM, set its properties, create View, assign its VM (in constructor), then display window. This is done using class that I named ViewController and this class have methods with parameters for every window in my application. I think there can be better solution than this, but not overengineered.
The normal solution is you have a class that wraps and instantiates a View ViewModel pair. This is often called screen. it would look something like this.
public class Screen<TView> where TView : Window
{
public Screen(TView view, object viewModel){
//store view and viewModel props
//display view
//set viewModel as DataContext of view
}
}
This is a very rough example, there are lots of ways you can do it.
In the last I created implementation of IWindowManager, which have methods for showing required windows and these methods have parameters if needed. Methods create view model, set its properties and inject it to window. Only drawback of this solution is when new window is needed, new method must be added to interface and implementation of WindowManager.

Prism, Unity, and Multiple Views ala MDI

I'm trying to create an application similar to Visual Studio in that we have a main content area (i.e. where documents are displayed in a TabControl, not a true MDI interface), with a menu on the side.
So far, I have everything working, except the content. My goal is that when a user double clicks on an item in the navigation menu on the side, it opens the document in the Content region. This works, but every time I double click it spawns a new instance of that same view. There's a chance that I could have multiple views of the same type (but different "names") in the TabControl content container.
Right now, my code looks something like this...
IRegion contentRegion = IRegionManager.Regions[RegionNames.ContentRegion];
object view = IUnityContainer.Resolve(viewModel.ViewType, viewModel.UniqueName);
if (!IUnityContainer.IsRegistered(viewModel.ViewType, viewModel.UniqueName))
{
IUnityContainer.RegisterInstance(viewModel.UniqueName, view);
contentRegion.Add(view);
}
contentRegion.Activate(view);
However, it appears that the view is never registered, even though I register it... I imagine I'm probably doing this wrong -- is there another way to do this? (re: the right way)
So, the problem was trying to do it this entire way. The smart method (for anyone else trying to do this) is to make use of Prism the correct way.
What I ended up doing was instead Navigating by:
1. In the Navigation Menu, constructing a UriQuery (included in Prism) with the UniqueID of the view I want to display (which is guaranteed to be unique) and adding that to the View I wanted to navigate to, i.e.:
IRegionManager.RequestNavigate(RegionNames.ContentRegion, new Uri(ViewNames.MyViewName + query.ToString(), UriKind.Relative));
where query is the UriQuery object.
2. Register the View and ViewName in the Module via:
IUnityContainer container = ServiceLocator.Current.GetInstance<IUnityContainer>();
container.RegisterType<object, MyView>(Infrastructure.ViewNames.MyViewName);
3. In the View, make sure the ViewModel is a parameter on the constructor. Let Prism inject this manually for us. Inside the constructor, make sure you set the DataContext to the incoming ViewModel.
4. Finally, make sure your ViewModel implements INavigationAware interface... This is a very simple implementation of it (UniqueID is a property on the ViewModel):
public virtual bool IsNavigationTarget(NavigationContext navigationContext)
{
if (navigationContext.Parameters != null)
return (navigationContext.Parameters["UniqueID"] == UniqueID);
return false;
}
public virtual void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public virtual void OnNavigatedTo(NavigationContext navigationContext)
{
if (navigationContext.Parameters != null)
UniqueID = navigationContext.Parameters["UniqueID"];
}
From here, Prism will ensure that only one view of your "UniqueID" will exists, while allowing for others of the same view, but different ViewModel (or data for that ViewModel, i.e. viewing two users in different tabs, but both use the same templated view).

User controls communicating via Commands - how?

I'm working on my first project in WPF/XAML, and there's a lot I've not figured out.
My problem is simple - I need a window that has a bunch of fields at the top, with which the user will enter his selection criteria, a retrieve button, and a data grid. When the user clicks on the button, a query is run, and the results are used to populate the grid.
Now the simple and obvious and wrong way to implement this is to have a single module containing a single window, and have everything contained within it - entry fields, data grid, the works. That kind of mangling of responsibilities makes for an unmaintainable mess.
So what I have is a window that is responsible for little more than layout, that contains two user controls - a criteria control that contains the entry fields and the retrieve button, and a data display control that contains the data grid.
The question is how to get the two talking to each other.
Years back, I would have added a function pointer to the criteria control. The window would have set it to point to a function in the display control, and when the button was clicked, it would have called into the display control, passing the selection criteria.
More recently, I would have added an event to the criteria control. I would have had the window set a handler in the display control to listen to the event, and when the button was clicked, it would have raised the event.
Both of these mechanisms would work, in WPF. But neither is very XAMLish. It looks to me like WPF has provided the ICommand interface specifically to accommodate these kinds of connection issues, but I've not yet really figured out how they are intended to work. And none of the examples I've seen seem to fit my simple scenario.
Can anyone give me some advice on how to fit ICommand to this problem? Or direct me to a decent explanation online?
Thanks!
MVVM is the prevalent pattern used with WPF and Silverlight development. You should have a read up on it.
Essentially, you would have a view model that exposes a command to perform the search. That same view model would also expose properties for each of your criteria fields. The view(s) would then bind to the various properties on the view model:
<TextBox Text="{Binding NameCriteria}"/>
...
<Button Command="{Binding SearchCommand}".../>
...
<DataGrid ItemsSource="{Binding Results}"/>
Where your view model would look something like:
public class MyViewModel : ViewModel
{
private readonly ICommand searchCommand;
private string nameCriteria;
public MyViewModel()
{
this.searchCommand = new DelegateCommand(this.OnSearch, this.CanSearch);
}
public ICommand SearchCommand
{
get { return this.searchCommand; }
}
public string NameCriteria
{
get { return this.nameCriteria; }
set
{
if (this.nameCriteria != value)
{
this.nameCriteria = value;
this.OnPropertyChanged(() => this.NameCriteria);
}
}
}
private void OnSearch()
{
// search logic, do in background with BackgroundWorker or TPL, then set Results property when done (omitted for brevity)
}
private bool CanSearch()
{
// whatever pre-conditions to searching you want here
return !string.IsEmpty(this.NameCriteria);
}
}

Pass Single Instance of Model to Multiple View Models in same Module for different views

I am working on a project using PRISM where I have left navigation implemented as Tree View and any click event happens raise event using event aggergation to Enrolment Module which has multiple view model for multiple views (like Wizard Applicaiton where you can go through many views to collect data). I want to have a common or shared or singleton model which can be passed across this view models and save at the end.... users can click on any link any navigation at any time and it should save data in to this singleton model expsosed through different view model. Do you have any samples which are doing something like this... or can you type up a quick one on how to do it? OR it is not possible to do it at all. I am following all patterns from Brian Lagunas's Pluralsight Video for PRISM so try to use that way....
I would have a MasterViewModel which controls the "wizard" pages and current state
It would contain the following properties:
List<ViewModelBase> Pages
int CurrentPageIndex
ViewModelBase CurrentPage, which returns Pages[CurrentPageIndex]
MyClass DataObject
The MasterView that goes with the MasterViewModel would be nothing more than a ContentControl with it's Content bound to CurrentPage. I would probably also define DataTemplates in the MasterView which tells WPF which View to draw with which Page
Your MasterViewModel would be in charge of handling the pages, and passing each page a reference to the data it needs. For example in the constructor it might say,
public MasterViewModel(MyClass dataObject)
{
DataObject = dataObject;
Pages.Add(new InfoPage(DataObject));
Pages.Add(new AddressPage(DataObject.Addresses));
Pages.Add(new PhonePage(DataObject.Phones));
Pages.Add(new SaveMyClassPage(DataObject));
CurrentPageIndex = 0;
}
I have an example here if you're interested
I don't know, is it prism way, or something another, when I build something like wizard, first of all I create instance of all data which wizard collect.
public WizardData wd = new WizardData();
Then, every page of wizard are initialized by this wd instance, i.e.
public FirstWizardPage(WizardData wd)
{
this.wizardData = wd;
}
So, this way allow you to have button Finish on every page, for example. You can initialize your ViewModel with wd, or its properties.
This way is not the best. Its hust one of the possible way.
Another - is to create singleton and use it without reference passing from page-to-page.
When you use Prism you also have a Dependency Injection Container, usually Unity or MEF. To solve your problem you can register your model as singleton to those DI containers. Every view model that asks the DI container to resolve their dependecy, in our special case the model, will get the singleton instance back from the DI container.
Unity example: You register your model as singleton instance:
public void Initialize( )
{
container.RegisterInstance<Model>(new Model(), new ContainerControlledLifetimeManager( ));
}
Now you can resolve your dependencies in your view model:
public ViewModel(IUnityContainer container)
{
Model model = container.Resolve<Model>();
}

Resources