WPF MVVM: How to avoid creating multiple instances of a ViewModel? - c#

I am writing a multi-page WPF app using MVVM and I'm having a problem I can't seem to solve effectively.
If there's an error at any point in the app, I've created single sub-view that can be shown inside a view to display the error and info on any page (View).
The way I'm trying to do this is:
The page (View) has a ViewModel that handles the business logic including determining something went wrong. It will then create an instance of the Error Sub-View inside a content control where the error will be displayed.
The ViewModel signals which error has occurred by calling a public method on the Sub-View's ViewModel and passes this function a byte index so the Error Sub-View can lookup and get the information to be displayed.
The Sub-ViewModel sets this error info in a property "ActiveError". ActiveError has a RaisePropertyChanged method and the Error Sub-View has text fields bound to this property to display the data.
Simple stuff. However, the Error Sub-View is displayed but never with any data.
I thought I solved this by making ActiveError static until I figured out what was really happening (I think). The ActiveError data isn't being shown because in this process, 3 instances of the Error Sub-ViewModel are being created.
One is created when we create the Sub-ViewModel:
Page ViewModel
New Sub-ViewModel instance
ConnectionErrorVM = new ConnectionErrorViewModel();
CurrentView = ConnectionErrorVM;
Page Xaml
New Sub-View instance
<Page.Resources>
<DataTemplate x:Name="ConnectionErrorViewTemplate"
DataType="{x:Type vm:ConnectionErrorViewModel}">
<v:ConnectionErrorView DataContext="{Binding}" />
</DataTemplate>
</Page.Resources> ...
//Further down the page in grid
<ContentControl x:Name="ConnectionMessagesView"
Content="{Binding CurrentView}"/>
And now the second is created here:
Sub-View Xaml
Second Sub-ViewModel
<UserControl.DataContext>
<vm:SystemErrors/>
</UserControl.DataContext>
And now the 3rd:
Page ViewModel###
Calling the SetActiveError method
SystemErrors error = new SystemErrors();
error.SetActiveError(errorCodeToIndex);
It seems like this is a useful approach for a lot of things in MVVM, but I can't think of a way to do it without this without the extra instances because the datacontext must always be set in the Sub-View and the PageView model will always have to create an instance to call a method. How do approach this without band-aids like singletons or being fine with doing things wrong because the static keyword saves us?

Related

Assign DataTemplate to view with view model in code behind

I'm trying to set a new DataTemplate as a new Window resource in my MainWindow derived from the System.Windows.Window class. The code for the XAML is quite simple and looks like this:
<Window.Resources>
<DataTemplate DataType="{x:Type model:MyViewModel}">
<view:MyView />
</DataTemplate>
</Window.Resources>
What I exactly do here?
I try to show my data (MyViewModel) in or as a specific view (MyView). So far I do understand. Otherwise I wouldn't see the form itself, but the view model as a string with my.namespace.MyViewModel in the window.
But programmatically I do not understand, how to achieve the same. I know, that I have to add a new DataTemplate to the resources of my window. For this I have to "tell" the DataTemplate, which view to use (for the representation) and which data I want to represent, right?
So it must be something with:
DataTemplate template = new DataTemplate();
template.DataType = typeof(MyViewModel);
// Something, something ...
this.Resources.add(...);
Is this the right way to go? Or am I completely wrong?
I searched the web for solutions and also my WPF book, but there are only XAML implementations.
Why do I do that?
I have a headered content control which loads view models dynamically. The problem here is, that the user controls are sometimes dynamic and in case of data presentation I need to assign a specific (dynamic created) view to the data. So I try to load the current static user controls also in the way shown above.
Is there a way to go?
Or is there a better way to achieve the same results?

C# WPF content switching

I have a Window, and there are three types of content that can be displayed (they are all of UserControl type):
Login view
App view
Error reporting
What's the most appropriate way to switch between these? My thought was to pass instance of Window in the constructor and then addressing it's content.
Content = new LoginView(this);
And then change the content from LoginView,
public LoginView(Window wnd){
InitializeComponents();
wnd.Content = new MainView(wnd);
}
But this wouldn't update the Window's content. Is it caused by the caller (LoginView) being the window's current content? If that's the case, what would be the proper way to handle such situation?
Also note that the snippet provided doesn't include any logic. I just left it as simple as required to demonstrate the issue I'm facing.
Basically the connection between those controls is such:
Login view
- when the application starts
- when the application window sends a request (to the server) that returns unauthorized
App view
- handles all the application's features
Error view
- replaces app/login view in case of an error and informs user about what to do
You should maybe look at the MVVM pattern. Usually every user control / view should have its own view model, when using the MVVM pattern. So you can define different Views inside a MainView. Like the following example shows:
<Window.Resources>
<DataTemplate x:Key="LoginView" DataType="{x:Type ViewModel:LoginViewModel}">
<local:LoginView />
</DataTemplate>
<DataTemplate x:Key="AppView" DataType="{x:Type ViewModel:AppViewModel}">
<local:AppView />
</DataTemplate>
<Window.Resources>
So in your code of the MainView, you have to pass the appropriate ViewModel in a ContentControl. This will reference the right View.
<ContentControl Content="{Binding LoginViewModel}" />
So the DataTemplate will be shown, depending on the xxxViewModel that is passed.

Dynamically load WPF View & View Model from DLL

I have a wizard created in WPF consisting of pages as UserControl objects. What I'm trying to do is to load plugins from .DLL files which contain the following:
A code file for the plugin logic.
A XAML user control which will present configuration options for the plugin, displayed in the main wizard.
A view model for the user control.
I've been able to successfully load in and instantiate the UserControl object as well as the View Model, and I have gotten to the stage where the Control appears in it's own wizard page as intended.
(This is probably the view model that's instantiated correctly, since I set the title for the wizard page in the view model and that all works fine)
The problem I'm getting is that the UserControl I've loaded from the DLL isn't displaying correctly. Instead of displaying the UserControl contents, it just shows the plaintext MyDLL.MyCustomUserControl from the line x:Class="MyDLL.MyCustomUserControl" where the actual user control should be.
The contents of my user control that I'm loading from the DLL is:
<UserControl x:Class="MyDLL.MyCustomUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<Label Content="This is the Plugin Options Page" />
<TextBox Text="{Binding Path=PluginStringText}" />
</StackPanel>
</UserControl>
The property PluginStringText exists in the View Model for this User Control.
I have a feeling I need to somehow assign or bind the View Model to the User Control after loading it in from the DLL.
As part of my wizard, there is a section where I define Data Templates (this is another UserControl which contains the Wizard's pages), but I don't know how to actually add extra templates during runtime. I have a feeling this is causing the issue.
Searching through many topics reveals I could probably do this through the code behind file for the view, but then I have no way of obtaining a reference to the view's code behind in the first place to call a method. Is there a way of adding a new DataTemplate entry from the View's View Model class?
<UserControl.Resources>
<DataTemplate DataType="{x:Type viewModel:ExistingPage1ViewModel}">
<view:ExistingPage1View />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:ExistingPage2ViewModel}">
<view:ExistingPage2View />
</DataTemplate>
<...>
Thanks in advance if someone could point me in the right direction
I found a way to resolve this.
The problem was as I had expected, there was no mapping between the ViewModel data type and the View in which to render the ViewModel with.
I discovered a useful guide here http://www.ikriv.com/dev/wpf/DataTemplateCreation/ which explains how to create a new data template within code, very similar to if you'd hard coded the template in the XAML.
This was all well and good, but I still had to find a way to call the method to create the data template.
Well - It turned out I didn't need to call the method at all in the code behind! I just put the method to create the data template directly in my view model which is responsible for populating the wizard's pages.
This also has the added benefit that I do not have to hard code my existing pages datatemplate's in the xaml.
I will add the code referenced at http://www.ikriv.com/dev/wpf/DataTemplateCreation/ for future reference:
The method to create the DataTemplate object:
DataTemplate CreateTemplate(Type viewModelType, Type viewType)
{
const string xamlTemplate = "<DataTemplate DataType=\"{{x:Type vm:{0}}}\"><v:{1} /></DataTemplate>";
var xaml = String.Format(xamlTemplate, viewModelType.Name, viewType.Name, viewModelType.Namespace, viewType.Namespace);
var context = new ParserContext();
context.XamlTypeMapper = new XamlTypeMapper(new string[0]);
context.XamlTypeMapper.AddMappingProcessingInstruction("vm", viewModelType.Namespace, viewModelType.Assembly.FullName);
context.XamlTypeMapper.AddMappingProcessingInstruction("v", viewType.Namespace, viewType.Assembly.FullName);
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
context.XmlnsDictionary.Add("vm", "vm");
context.XmlnsDictionary.Add("v", "v");
var template = (DataTemplate)XamlReader.Parse(xaml, context);
return template;
}
This DataTemplate then needs to be registered as an application resource:
Registering the DataTemplate:
Application.Current.Resources.Add(template.DataTemplateKey;, template);
Once again, this code was provided thanks to Ivan Krivyakov at http://www.ikriv.com/dev/wpf/DataTemplateCreation/

MVVM and View/ViewModel hierarchy

I'm working on making my first game using C# and XAML for Windows 8. I'm still learning the core concepts and best practices, and MVVM has been a hurdle. I'll attempt to ask the question in two parts.
Background
The game I'm making is Sudoku. Sudoku has a board that contains a 9x9 grid of tiles. I have three models - Game, Board, and Tile. When a Game is created, it automatically creates a Board, and when the Board is created, it creates 81 (9x9) Tiles.
1. With a hierarchy of views, how are corresponding view models created?
To match the hierarchy of models, I would like to have a hierarchy of views (GameView contains a BoardView which contains 81 TileViews). In XAML, it's pretty easy to create this hierarchy of views with user controls, but I don't understand how the view models get created.
In the examples I've seen, the data context of a user control is often set to the view model (using the ViewModelLocator as a source) which creates a fresh instance of the view model. This seems to work well if you have a flat view, but also seems like it gets messy when you have a hierarchy. Does the GameView create a GameViewModel and leave it up to its BoardView child to create a BoardViewModel? If so, how does the GameViewModel communicate with the BoardViewModel? Can the BoardViewModel communicate back up the hierarchy to the GameViewModel?
2. How does a view model get model data?
In iOS, I would start by using a service to fetch a Game model that was pre-populated with data. I would then create a GameViewController view controller (which was in charge of creating the view) and pass the Game to it. In MVVM, I see the value in having a view be in charge of creating its own view model (ideally using a ViewModelLocator), but I don't understand how that view model gets the model.
In all of the examples I've found online, the view model uses some service to fetch its own data. But I haven't come across any example that accepts constructor params or params passed from a higher level of navigation. How is this done?
I don't want to use an application resource or some other kind of singleton storage method for my model because, not that I do, but what if I wanted to display multiple puzzles on the screen at once? Each GameView should contain its own Game.
Not only does the GameViewModel need a reference to the Game model, but the BoardViewModel that was created somehow (see question 1) needs a reference to the Board model that belongs to the Game model. The same goes for all the Tiles. How is all this information passed down the chain? Can I do this much heavy lifting entirely within XAML, or am I going to have to do some sort of binding or other initialization in code?
Phew!
I appreciate any advice you can give, even if it's not a full answer. I'm also keen to find any examples of MVVM projects that share similar challenges to my own. Thanks a ton!
I would start by creating a class to begin the application with. Typically I call that class something like ApplicationViewModel or ShellViewModel, even though technically it can abide by different rules than what I would typically use for a ViewModel
This class gets instantiated at startup, and is the DataContext for the ShellView or ApplicationView
// App.xaml.cs
private void OnStartup(object sender, StartupEventArgs e)
{
var shellVM = new ShellViewModel();
var shellView = new ShellView();
shellView.DataContext = shellVM;
shellView.Show();
}
This is usually the only place I set a DataContext for a UI component directly. From this point on, your ViewModels are the application. Its important to keep this in mind when working with MVVM. Your Views are simply a user friendly interface that allows users to interact with the ViewModels. They're not actually considered part of the application code.
For example, your ShellViewModel may contain:
BoardViewModel CurrentBoard
UserViewModel CurrentUser
ICommand NewGameCommand
ICommand ExitCommand
and your ShellView might contain something like this:
<DockPanel>
<Button Command="{Binding NewGameCommand}"
Content="New Game" DockPanel.Dock="Top" />
<ContentControl Content="{Binding CurrentBoard}" />
</DockPanel>
This will actually render your BoardViewModel object into the UI as the ContentControl.Content. To specify how to draw your BoardViewModel, you can either specify a DataTemplate in ContentControl.ContentTemplate, or use implicit DataTemplates.
An implicit DataTemplate is simply a DataTemplate for a class that doesn't have an x:Key associated with it. WPF will use this template anytime it encounters an object of the specified class in the UI.
So using
<Window.Resources>
<DataTemplate DataType="{x:Type local:BoardViewModel}">
<local:BoardView />
</DataTemplate>
</Window.Resources>
will mean that instead of drawing
<ContentControl>
BoardViewModel
</ContentControl>
it will draw
<ContentControl>
<local:BoardView />
</ContentControl>
Now the BoardView could contain something like
<ItemsControl ItemsSource="{Binding Squares}">
<ItemsControl.ItemTemplate>
<ItemsPanelTemplate>
<UniformGrid Rows="3" Columns="3" />
</ItemsPanelTemplate>
<ItemsControl.ItemTemplate>
</ItemsControl>
and it would draw a board using a 3x3 UniformGrid, with each cell containing the contents of your Squares array. If your BoardViewModel.Squares property happened to be an array of TileModel objects, then each grid cell would contain a TileModel, and you could again use an implicit DataTemplate to tell WPF how to draw each TileModel
Now as for how your ViewModel gets its actual data objects, that's up to you. I prefer to abstract all data access behind a class such as a Repository, and have my ViewModel simply call something like SodokuRepository.GetSavedGame(gameId);. It makes the application easy to test and maintain.
However you get your data, keep in mind that the ViewModel and Models are your application, so they should be responsible for getting data. Don't do that in the View. Personally I like keeping my Model layer for plain objects that hold data only, so only ever perform data access operations from my ViewModels.
For communication between ViewModels, I actually have an article on my blog about that. To summarize, use a messaging system such as Microsoft Prism's EventAggregator or MVVM Light's Messenger. They work like a kind of paging system: any class can subscribe to receive messages of a specific type, and any class can broadcast messages.
For example, your ShellViewModel might subscribe to receive ExitProgram messages and close the application when it hears one, and you can broadcast an ExitProgram message from anywhere in your application.
I suppose another method would be to just attach handlers from one class to another, such as calling CurrentBoardViewModel.ExitCommand += Exit; from the ShellViewModel, but I find that messy and prefer using a messaging system.
Anyways, I hope that answers some of your questions and will point you in the right direction. Goodluck with your project :)

Loading XAML at runtime using the MVVM pattern in WPF

This is a question that extends from the originally posted here:
Link to loading-xaml through runtime
I'm working on a WPF MVVM application that loads XAML content dynamically from an external source, very similar as the answer in the post above.
Here is what I got so far:
My View declares an instance of the ViewModel as a resource and creates an instance of that ViewModel
In my ViewModel constructor I'm loading a XamlString property coming from an external source (file or db..)
In my view I have a button that user clicks after ViewModel finishes loading and in the click-event code-behind I'm deserializing the dynamically loaded XAML and add it to my grid.
My question is, how can I eliminate code-behind and automate the logic so the View can render the new xaml section dynamically right after the ViewModel is done getting the XAML content and initializing the string property?
Should I use some kind of Messaging Bus so the ViewModel notifies once the property has been set so the View can add the new content?
What troubles me is the fact that ViewModels do have a reference to Views and should not be in charge of generating UI elements.
Thanks in advance!
Edit:
Just to clarify: in my particular case I am not trying to bind a Business Object or Collection (Model) to a UI element (e.g. Grid) which obviously could be accomplished through templates and binding. My ViewModel is retrieving a whole XAML Form from an external source and setting it as a string property available to the View. My question is: Who should be in charge of deserializing this XAML string property into a UI element and add it programmatically to the my grid once my Xaml string property in the VM is set?
This sounds to me more of like a View responsibility, not ViewModel. But the pattern as i understand it enforces to replace any code-behind logic with V-VM bindings.
I have a working solution now and I'd like to share it. Unfortunately I did not get rid of code-behind completely but it works as I expect it to. Here is how it works(simplified):
I have my simplified ViewModel:
public class MyViewModel : ViewModelBase
{
//This property implements INPC and triggers notification on Set
public string XamlViewData {get;set;}
public ViewModel()
{
GetXamlFormData();
}
//Gets the XAML Form from an external source (e.g. Database, File System)
public void GetXamlFormData()
{
//Set the Xaml String property
XamlViewData = //Logic to get XAML string from external source
}
}
Now my View:
<UserControl.Resources>
<ViewModel:MyViewModel x:Key="Model"></ViewModel:MyViewModel>
</UserControl.Resources>
<Grid DataContext="{StaticResource Model}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel>
<!-- This is the Grid used as a Place Holder to populate the dynamic content!-->
<Grid x:Name="content" Grid.Row="1" Margin="2"/>
<!-- Then create a Hidden TextBlock bound to my XamlString property. Right after binding happens I will trigger an event handled in the code-behind -->
<TextBlock Name="tb_XamlString" Text="{Binding Path=XamlViewData, Mode=TwoWay, UpdateSourceTrigger=LostFocus, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Visibility="Hidden" Loaded="tb_XamlString_Loaded" />
</StackPanel>
</Grid>
Basically I created a hidden TextBlock bound to my XAML String property in the ViewModel and I hooked its Loaded event to an event handler in the code behind of the View:
private void tb_XamlString_Loaded(object sender, RoutedEventArgs routedEventArgs)
{
//First get the ViewModel from DataContext
MyViewModel vm = content.DataContext as MyViewModel;
FrameworkElement rootObject = XamlReader.Parse(vm.XamlViewData) as FrameworkElement;
//Add the XAML portion to the Grid content to render the XAML form dynamically!
content.Children.Add(rootObject);
}
This may not be the most elegant but gets the job done. Like some people say, in MVVM there are some cases like this where little code-behind code is needed. It doesn't hurt and also part of this solution still uses the V-VM Binding principles when using the VM to retrieve and populate the XamlString property and exposing it to the View. If we would like to Unit Test the XAML parsing and loading functionality we could delegate it to a separate class.
I hope someone finds this useful!
I'm having trouble understanding what you're saying, so my answer will be based on my interpretation. You should consider posting a sample (simplified) of what you're trying to do.
1) I think you're misunderstanding what MVVM does. MVVM is mostly a binding-based pattern. Your view model should be exposing properties containing business objects and your view should just be binding to those properties. If I am misunderstanding you, and that's what you are doing, then your problem is that your view needs to be aware of when the properties get updated (after you deserialize your xaml, etc). There are two ways to do this: INotifyPropertyChanged interface on your viewmodel, or make your view model inherit from DependencyObject, and make the properties dependency properties. I won't go into details here, because this is a large subject that you should research on Google before making a decision.
2) Generally speaking, you shouldn't use click events inside your view if you're using MVVM. Instead, create properties on the view model of type ICommand (and create ICommand implementations to match, or use an implementation of DelegateCommand (google it) which will allow you to use delegates to implement the interface. The idea is, your view binds to the property and executes the handler directly inside the viewmodel.
3) If you want to push information from the viewmodel to the view, then you should create an event on the viewmodel and subscribe to it in the view, but this is a last resort, only to be used in cases like displaying a new window, etc. Generally, you should be using binding.
4) To be more specific about what you're doing, you should be binding your Grid's ItemsSource property to some property on the view model. Note, the property on the view model should be of type ObservableCollection<T> if you want to be able to add items and get instant updates.
Hope this helps.

Categories