inherit or encapsulate View/Viewmodel - c#

I want to encapsulate a view/viewmodel into another view/viewmodel. The aim is to have a popup, slide panel or whatever you can think of common behaviour and insert a custom view/viewmodel into it.
I want to use the generic for doing that but I'm kind of stuck
the parent view model will look like that
public class SidePanelViewModel<T>
{
public SidePanelViewModel(T enclosedViewModel)
{
EnclosedViewModel = enclosedViewModel;
}
public T EnclosedViewModel { get; private set; }
}
The parent view will be like that
... whatever design and behavior ...
<ContentPresenter Content="{Binding EnclosedViewModel}"/>
... whatever design and behavior ...
In my mapping file I would like to put that but here is the problem:
<DataTemplate DataType="{x:Type WPFTests:SidePanelViewModel}">
<WPFTests:SidePanelView />
</DataTemplate>
<DataTemplate DataType="{x:Type WPFTests:EnclosedViewModel}">
<WPFTests:EnclosedView />
</DataTemplate>
This does not work because the constructor of the viewmodel SidePanelViewModel requiere a type to be created.
I guess what I want to do is having a view/viewmodel inherit from another view/viewmodel
Hope someone can help on that

Why do you need to use <T>?
Can't you just use a regular object or some kind of shared base such as IViewModel or ViewModelBase?

Working with generic classes in WPF can be quite painful sometimes ..
I would recommend you to create a base (abstract) class that implements the default behavior for your (virtual, of course) methods.

Related

How to set DataTemplate for class inheriting more than one interface?

I am trying to use an MVVM architecture and MEF in order to build by application. I want to use DataTemplates and ContentControls in order to allow my application to display to the user in as generic a way as possible.
So I am now creating the ability for a user to read/write information and read/write results to somewhere, could be XML, could be a database. So I have two interfaces IResultStorage and ITestStorage
I now want to create a page for the user to update settings for these, so file location or database etc. My view model imports them via MEF:
public sealed class AdminViewModel : ViewModelBase
{
[Import]
public ITestStorage TestStorage { get; set; }
[Import]
public IResultStorage ResultStorage { get; set; }
}
Then the view is exported and loaded into the Resources.MergedDictionaries at run time
<DataTemplate DataType="{x:Type vm:AdminViewModel}">
<Grid>
<TabControl Grid.Row="0">
<TabItem Header="Tests">
<ContentControl Grid.Row="0" Content="{Binding TestStorage}"/>
</TabItem>
<TabItem Header="Results">
<ContentControl Grid.Row="0" Content="{Binding ResultStorage}"/>
</TabItem>
</TabControl>
</Grid>
</DataTemplate>
However, the way I currently have it implemented is that one class has inherited both of these and it is this that is causing me problems:
[Export(typeof(ITestStorage))]
[Export(typeof(IResultStorage))]
public sealed class XmlStorage : ITestStorage, IResultStorage { ... }
So when the AdminViewModel above gets drawn both ContentControls are of type XmlStorage it seems so I don't know how to create DataTemplates to draw them properly.
Hopefully this makes sense, if I have done it totally the wrong way it would be good to know.
Well for a more tricky implementation, let us called it more intelligent implementation I would suggest a TemplateSelector. For more information please have look here.
You will be able to assign templates based on the type of the given VM or business object. The only challenge you will face is the fact that you have to find out in which 'role' the object is passed to the TemplateSelector.
Additional info
I think this will help you, too.
have you testet if you create subDatatemplates for each as Resourse?
<DataTemplate DataType="{x:Type vm:TestStorage}">
<Grid>
<Label Content="{Binding someValueFromTestStorage}"/>
</Grid>
</DataTemplate>
EDIT
maybe this 2 links could help you First, Second (ger)
also this link could be interesting follow Beatriz Costa - MSFT (Partner)

GUI idea for WPF, though no clue how to implement it

What I've got is basicly two classes Plugin and PluginLauncher
Plugin is an abstract class that implements some functions to make a class a Plugin for my PluginLauncher class.
PluginLauncher is a class that holds a collection (SortedDictionary) including some helper functions to start, stop, restart all or a specific Plugin.
It also loads all plugins on initialisation. Each plugin can be a .exe or .dll with a class the inherits from Plugin.
An AppDomain is created for each plugin and communication is also setup for each Plugin (through a simple IPC messaging through sockets) (still has to be implemented)
I want to have a very, VERY basic GUI implementation that just has a list of all loaded Plugins, noting the plugin name, its state (which can be running, stopping, stopped, stoppedprematurely (an Enum)) and a button per plugin to start, stop or restart it.
I know I can add this functionality programmaticaly by just placing the elements on the GUI and calculating each X/Y location etc. but I am sure WPF has some pre-made 'functionalities' for this. But I am quite new to WPF and have no clue where to start looking.
A simple note: I am limited to .net 3.5 (or lower) thus no 4.0 elements.
I've included a very simple (hooray mspaint skills) example of what I had in mind.
The plugin nature of your app has little bearing on the mechanics of how you achieve this. Essentially, you need a collection of view models. Each item in that collection represents a plugin (but it could equally represent a customer or a chicken drumstick). You then bind an ItemsControl to this collection and define a template for how the item should be rendered.
Here is some pseudo-code to get you on your way:
public class PluginViewModel : ViewModel
{
public string Name { get; }
public PluginState State { get; private set; }
public ICommand StartCommand { get; }
public ICommand StopCommand { get; }
public ICommand RestartCommand { get; }
}
public class PluginLauncherViewModel : ViewModel
{
// use an ObservableCollection<PluginViewModel> to store your plugin view models
public ICollection<PluginViewModel> Plugins { get; }
}
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Plugins}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<UniformGrid Rows="1">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Status}"/>
<Button Command="{Binding StartCommand}">Start</Button>
<Button Command="{Binding StopCommand}">Stop</Button>
<Button Command="{Binding RestartCommand}">Restart</Button>
</UniformGrid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
Some problems you will no doubt bump into:
the DataContext of the outer XAML (ie. the ScrollViewer in my above example) must be an instance of PluginLauncherViewModel. How you wire this up is up to you, and there are varied options. Start with something simple like setting it in your code-behind.
ViewModel is a base class for all view models. See here for an example.
Your implementation of ICommand should be MVVM friendly. See here for an example.
For the simplest approach you might consider the Table element. For a more fine-grained control I'd recommend you using a Grid.

How can I switch views between viewmodels?

My Main window defines the markup for the application, for this specific scenario lets say I have a grid with 2 columns.
First column will have navigation links, and second column will display the different views.
There are 2 views (and 2 viewmodels) defined in mainwindow xaml:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:Window1ViewModel}">
<vw:Window1View/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:Window2ViewModel}">
<vw:Window2View/>
</DataTemplate>
</Window.Resources>
And in second grid column that displays the views i got :
<ContentControl Content="{Binding Path=ViewModel}" HorizontalAlignment="Left">
</ContentControl>
Where ViewModel is a property that I set accordingly to a view(viewmodel) that i want to display.
Like :
ViewModel = new Window1ViewModel();
(datacontext of the mainwindowview is set to MainWindowViewModel)
So there is no problem to switch between views from the MainWindowViewModel.
My problem is how to switch within Window1ViewModel into Window1ViewMode2?
The various ViewModels don't "know" about other ViewModels.
Only MainWindowViewModel knos about others...
How can I solve this?
Maybe I should define a custom Event (with parameter), MainWindowViewModel will subscribe and other viewmodels will trigger it and then MainWindowViewModel will switch to the needed view?
the solution you describe is one possibility. One other I can think of is using some kind of Navigation-Service (static class or interface you pass to all your child-Viewmodels) that do this kind of work.
If your MainWindowViewModel creates all the others I would stick to the interface solution. You can for example let the MainWindowVM implement such a interface and inject it into all the child-vm on creation. This is much the same as your event-approach but instead of the childs-providing and the main having to subscribe you have the main give something ... IMHO the better approach.
Ok, may be I understood your point. You want that controller actually be modelview which notifies to mainmodelview about the fact that it have to be swapped with someone else.
Considering that we are talking about WPF, create DependecyProperty on mainmodelview , and set it from childview, which in code behind will trigger modelviews swap.

Including partial views when applying the Mode-View-ViewModel design pattern

Consider that I have an application that just handles Messages and Users I want my Window to have a common Menu and an area where the current View is displayed.
I can only work with either Messages or Users so I cannot work simultaniously with both Views. Therefore I have the following Controls
MessageView.xaml
UserView.xaml
Just to make it a bit easier, both the Message Model and the User Model looks like this:
Name
Description
Now, I have the following three ViewModels:
MainWindowViewModel
UsersViewModel
MessagesViewModel
The UsersViewModel and the MessagesViewModel both just fetch an ObserverableCollection<T> of its regarding Model which is bound in the corresponding View like this:
<DataGrid ItemSource="{Binding ModelCollection}" />
The MainWindowViewModel hooks up two different Commands that have implemented ICommand that looks something like the following:
public class ShowMessagesCommand : ICommand
{
private ViewModelBase ViewModel { get; set; }
public ShowMessagesCommand (ViewModelBase viewModel)
{
ViewModel = viewModel;
}
public void Execute(object parameter)
{
var viewModel = new ProductsViewModel();
ViewModel.PartialViewModel = new MessageView { DataContext = viewModel };
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
And there is another one a like it that will show Users. Now this introduced ViewModelBase which only holds the following:
public UIElement PartialViewModel
{
get { return (UIElement)GetValue(PartialViewModelProperty); }
set { SetValue(PartialViewModelProperty, value); }
}
public static readonly DependencyProperty PartialViewModelProperty =
DependencyProperty.Register("PartialViewModel", typeof(UIElement), typeof(ViewModelBase), new UIPropertyMetadata(null));
This dependency property is used in the MainWindow.xaml to display the User Control dynamicly like this:
<UserControl Content="{Binding PartialViewModel}" />
There are also two buttons on this Window that fires the Commands:
ShowMessagesCommand
ShowUsersCommand
And when these are fired, the UserControl changes because PartialViewModel is a dependency property.
I want to know if this is bad practice? Should I not inject the User Control like this? Is there another "better" alternative that corresponds better with the design pattern? Or is this a nice way of including partial views?
why not use a ContentPresenter/ContentControl with a datatemplate in your mainwindow?
instead of UserControl Content="{Binding PartialViewModel}" />, you can use a:
<ContentPresenter Content="{Binding Path=PartialViewModel}" />
all you have to do: is set your PartialViewmodel to your child viewmodel and create a datatemplate, so wpf will know how to render your childviewmodel
<DataTemplate DataType={x:Type UserViewModel}>
<UserView/>
</DataTemplate>
<DataTemplate DataType={x:Type MessageViewModel}>
<MessageView/>
</DataTemplate>
when ever you set your PartialViewmodel in your MainViewmodel, the right View will render in your ContenControl.
Edit 1
at least you have to implement INotifyPropertyChanged in your ViewModel and fire it when ever the PartViewModel property is set.
Edit 2
if you use Commands in your viewmodels take a look at some mvvm framework implementations like DelegateCommand or RelayCommand. handling ICommand become much easier with this. within your mainviewmodel you can create commands simple like that
private DelegateCommand _showMessageCommand;
public ICommand ShowMessageCommand
{
get
{
return this._showMessageCommand ?? (this._showMessageCommand = new DelegateCommand(this.ShowMessageExecute, this.CanShowMessageExecute));
}
}
This isn't a bad approach at first sight, it might be just fine to use in a small app.
However, there are a couple of things that aren't that nice:
ViewModelBase needs to be a DependencyObject to have a DependencyProperty. In the real world I 've found that it's very annoying to have to treat ViewModels in a single-threaded manner (there are lots of async operations one might want to perform).
It doesn't scale; changing the layout will require significant amounts of work.
Any decent MVVM framework makes UI composition easy by providing infrastructure to compose sub-Views into your main View. In Prism (which is my personal preference), this happens with Regions.
I would look at using an MVVM framework such as Caliburn.Micro which makes view composition incredibly easy. If you have a property on your view model which is a view model type, and a ContentControl on your view which is named the same as your property, then Caliburn.Micro will locate that view models corresponding view via conventions, do the binding for you automatically, and inject the view into the ContentControl.
I would also avoid using dependency properties on your view models, and instead implement INotifyPropertyChanged. Caliburn.Micro comes with a PropertyChangedBase type which implements this interface, and also provides a helper method for invoking the PropertyChanged event using lambda expressions rather than magic strings (which is much better for refactoring later).
EDIT
http://msdn.microsoft.com/en-us/library/ms743695.aspx shows an example of implementing INotifyPropertyChanged.
To achieve what you want to do in Caliburn.Micro, you would do something like the following (a crude example, but it shows you how easy it is doing view composition using an MVVM framework):
public class MainViewModel : Conductor<IScreen>.Collection.OneActive
{
private UsersViewModel usersViewModel;
private MessagesViewModel messagesViewModel;
public UsersViewModel UsersViewModel
{
get { return this.usersViewModel; }
set { this.usersViewModel = value; this.NotifyOfPropertyChanged(() => this.UsersViewModel);
}
public MessagesViewModel MessagesViewModel
{
get { return this.messagesViewModel; }
set { this.messagesViewModel = value; this.NotifyOfPropertyChanged(() => this.MessagesViewModel);
}
public MainViewModel()
{
this.UsersViewModel = new UsersViewModel();
this.MessagesViewModel = new MessagesViewModel();
this.Items.Add(this.UsersViewModel);
this.Items.Add(this.MessagesViewModel);
// set default view
this.ActivateItem(this.UsersViewModel);
}
public ShowUsers()
{
this.ActivateItem(this.UsersViewModel);
}
public ShowMessages()
{
this.ActivateItem(this.MessagesViewModel);
}
}
Note that UsersViewModel and MessagesViewModel would derive from Screen.
To invoke the ShowUsers or ShowMessages verbs with Caliburn.Micro, you just need to create view controls with the same name. The conductor type has an ActiveItem property which is the currently conducted item, so you can add a ContentControl to your MainView.xaml which is named ActiveItem, and Caliburn.Micro will take care of injecting the correct view.
So your MainView.xaml may look like:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinition>
<!-- Menu in left hand column -->
<StackPanel Grid.Column="0">
<Button x:Name="ShowUsers">Show Users</Button>
<Button x:Name="ShowMessages">Show Messages</Button>
</StackPanel>
<!-- Currently active item -->
<ContentControl x:Name="ActiveItem" Grid.Column="1" />
</Grid>
you should take a look at prism. It gives you region handling.
I would also take a look at MEF to Export Views and on this way maintain an extensibility for your project.

Decouple the screens without magic strings

My WPF project will be organised like this :
Screens
Group1
Screen1
View.xaml
ViewModel.cs
Group2
Screen2
View.xaml
ViewModel.cs
To show the Screen1 from the Screen2 I'll use something like this: ScreenManager.Show("Group1.Screen1") This looks (using reflection) in the Screens.Group1.Screen1 namespace for a View and a ViewModel and instantiates them.
How can I eliminate the magic string without coupling Screen1 and Screen2 (I don't want the classes in Screen2 to use the Screen1 namespace). Also I would like some kind of screen discovery (autocompletion/intellisense)
Or maybe some way (automate test) to verify that all calls to ScreenManager.Show are valid.
Update :
I came up with this:
public class ScreenNames
{
public Group1Screens Group1;
public class Group1Screens
{
public ScreenName Screen1;
}
}
public sealed class ScreenName
{
private ScreenName() { }
}
public class ScreenManager : IScreenManager
{
public void Show(Expression<Func<ScreenNames, ScreenName>> x) {}
}
Usage:
screenManager.Show(x=>x.Group1.Screen1);
Not ideal but I suppose violating DRY is still better than magic strings. And I can automatically test (with reflection) that all calls are valid.
You don't need all that ScreenManager stuff in WPF, because the DataTemplate engine can take care of this for you with pure markup.
You can simply databind a particular area of your application with a ContentPresenter and a bunch of DataTemplates. Bind the area to a property of a 'root' ViewModel, and let the 'root' ViewModel implement INotifyPropertyChanged so that WPF knows if you change the ViewModel in that area.
public class RootViewModel : INotifyPropertyChanged
{
public object Screen1ViewModel { get; }
public object Screen2ViewModel { get; }
}
Databind one ContentPresenter control to the Screen1ViewModel property using
<ContentControl Content="{Binding Path=Screen1ViewModel}" />
and similarly for the next one. When you need to change the content of Screen1, you simply re-assign Screen1ViewModel from code, and because of the raised PropertyChanged event, WPF will pick it up and bind the new ViewModel to a new View.
The DataTemplates may be as simple as this:
<Window.Resources>
<DataTemplate DataType="{x:Type foo:MyViewModel}">
<self:MyControl />
</DataTemplate>
<DataTemplate DataType="{x:Type foo:MyOtherViewModel}">
<self:MyOtherControl />
</DataTemplate>
</Window.Resources>
In case you are not familiar with it, this article on MVVM in WPF is an excellent introduction.
Finally I used T4 code generation to generate my ScreenNames class. I did that by adapting this code : Auto generate strong typed navigation class for all user controls in ASP.NET web application

Categories