Wrap a view and view model to add cross-cutting concerns - wpf

In non-ui code I'm used to add decorators/wrappers around instances of interfaces to add cross-cutting concerns like logging, checking etc.
Now I'm looking for a way to do something similar for ui code, which is view-model first in my application.
An example:
I have a sample model like this
public class SampleViewModel
{
// some properties
}
and a data template for it
<DataTemplate DataType={x:Type local:SampleViewModel>
<!-- some content -->
</DataTemplate>
In some cases I need to add a border around a view for a view model which can be animated to highlight the area for instance.
This border could be added to virtually every view, so adding the border to this specific data template is no option.
My initial approach was adding a wrapper view model like this:
public class BlinkingBorderWrapperViewModel
{
public BlinkingBorderWrapperViewModel(IWantBeAbleToBlink innerVM)
{
InnerVM = innerVM;
}
public object InnerVM {get;private set;}
// expose properties relevant for the blinking part
// some may be retrieved via the interface `IWantBeAbleToBlink`
}
and a data template
<DataTemplate DataType={x:Type local:BlinkingBorderWrapperViewModel}>
<!-- draw the wrapping stuff -->
<Border ...>
<!-- include the wrapped vm -->
<ContentControl Content={Binding Path=InnerVM} />
<Border ...>
</DataTemplate>
The interface IWantBeAbleToBlink is used to exchange data with the wrapper (e.g. if it should blink right now) and as a tag to check if it should be wrapped.
This works so far for a single wrapper with these drawbacks:
the view model needs to store the wrapped sub-viewmodels and thus looses type information (the wrapper can be no template due to data template restrictions).
I have no clue how to add a second, third, ... wrapper in a way that is maintainable.
I might add several wrappers, but this would have to be done from the view's side (i.e. in XAML and not from the view model)
Is there a way to achieve this adding of cross-cutting concerns to view (models) that allows several aspects to be added and is usable with an view model first approach?
Other stuff I might want to add:
An anchor tag so the view (model) can be navigated to
A wrapper logging mouse moves
A wrapper for enabling/disabling
...
from the comments:
Leaking UI logic?
In my opinion the view model should be able to express the need to "be highlighted". This could be done by blinking, a change in the background, change in font-size or whatever the view implements.
Since the view model should not care how this is done (and the way the highlighting is performed should be changeable without having a need to adjust the view model), I'd like just to provide an interface better named IWantToBeAbleToBeHighlighted (instead of IWantBeAbleToBlink) with maybe just a single switch.
The concrete properties exposed to the view (blink frequence, or new font size or new background color) should be included in the wrapper view model.

Related

Reusing User Controls

I'm trying to alter an existing WPF application and my lack of WPF experience (I'm WINFORMS) is making this difficult.
I've come across a situation where I need to reuse a UserControl and I'm not sure how to do this in terms of modifying the xaml. I'll explain.
UserControlA has the following code:
<Grid Name="gdMain" Style="{StaticResource ContentRoot}">
<content:MonitorAlarmsPage />
</Grid>
Now, "MonitorAlarmsPage" is an AXML document that defines another UserControl - UserControlB. This UserControl, once created has to persist for the lifetime of the application.
So, I could have many UserControlAs, but only ONE UserControlB.
I've created a static class that has an appropriate UserControlB field which is updated when UserControlB is created, but how do I modify the content:MonitorAlarmsPage so that the content of the grid is replaced by the existing UserControlB as referenced in this static class and not by the XAML file that defines UserControlB? Can this actually be done? i.e. essentially, insert pre created user controls inside an XAML page.
To make things a bit clearer, UserControlB is essentially a page that can sit inside another page. The page is complex and there is a massive overhead incurred when it is created and so must only be created once.
WPF is very different to WinForms, so you certainly have your work cut out for you. The basic idea for the solution to your problem is this. You'll need to add a DependencyProperty to the MonitorAlarmsPage control. This will enable you to data bind to this property from outside the control.. This property should be of a type of custom class that you define, that contains all of the properties required in the inner control.
The next stage is to develop a DataTemplate that defines how WPF should display your custom class when it comes across an instance of it. In this DataTemplate, you declare your inner control, so that when WPF sees the custom class, it displays the inner control and sets the custom class as the DataContext for the inner control:
<DataTemplate DataType="{x:Type YourXmlNamespacePrefix:YourCustomClass}">
<YourControlsXmlNamespacePrefix:YourInnerControl DataContext="{Binding}" />
</DataTemplate>
Finally, you'll need to data bind your custom class (the static class) to the outer UserControl:
<content:MonitorAlarmsPage YourCustomClassProperty="{Binding YourStaticClass}" />
So just to be clear... this static class should be a data class, not a UI class. When using WPF, we manipulate data, not UI elements. We let the wonderful templating system generate the UI for us.

How one approaches designing a view with MVVM best practices in mind

This question is about how to factor the app design with MVVM in mind. I understand the general concepts of MVVM (ViewModel is not aware of the View, Models represent domain objects, etc...). I'm also aware of Commands and how some controls can invoke commands on the ViewModel.
What I can't figure out is how to factor a real-life app to fit this model (as opposed to simple textbox/button/query database examples). Here are some questions:
In a main view I have preview section that displays contextual data depending on what the user is doing (i.e. by interacting with certain controls, show some preview section). Should the View contain all the possible previews in XAML and then show/hide/update? Should ViewModel have properties like "public bool ShowPreviewA" and "public bool ShowPreviewB" set by ViewModel's internal state machine?
Where should the logic live when some controls have intricate interactions between them. For example, 3 checkboxes where at least one must be selected, by preventing unchecking. Seems to me that it would pollute the ViewModel on one hand, and also feel like there is certain "knowledge" about the View on the other hand.
This question is difficult to articulate properly (if I could, I would probably understand MVVM much better).
Any tips are welcomed.
EDIT:
The real question is how to break down the problem when writing the functionality of ViewModels. Is it a combination of top-down - i.e. encode every possible View state in ViewModel properties - and bottom-up - i.e. each logically related set of controls receive their properties from child ViewModels that report "up" some logical state (e.g. valid input)?
In a main view I have preview section that displays contextual data
depending on what the user is doing (i.e. by interacting with certain
controls, show some preview section). Should the View contain all the
possible previews in XAML and then show/hide/update? Should ViewModel
have properties like "public bool ShowPreviewA" and "public bool
ShowPreviewB" set by ViewModel's internal state machine?
No, of course not, if these "previews" are completely different UIs with completely different data, then use DataTemplates.
For example:
Given some classes:
public class Person: BusinessEntity //BusinessEntity is just a fictional base class for Model classes
{
public string LastName {get;set;}
}
public class Product: BusinessEntity
{
public string ProductName {get;set;
}
suppose your ViewModel is defined like this:
public class SomeViewModel: ViewModelBase //Same comment as above
{
public BusinessEntity SelectedEntity {get;set;} //NotifyPropertyChanged() etc
}
Your XAML can be defined like this:
<Window ...>
<Window.Resources>
<!-- DataTemplate for Person class -->
<DataTemplate DataType="Person">
<TextBox Text="{Binding LastName}"/>
</DataTemplate>
<!-- DataTemplate for Product class -->
<DataTemplate DataType="Product">
<TextBox Text="{Binding ProductName}"/>
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding SelectedEntity}"/>
</Window>
WPF will take care of rendering the appropiate DataTemplate inside the ContentPresenter depending on which object type is put in the SelectedEntity property in the ViewModel.
Where should the logic live when some controls have intricate
interactions between them. For example, 3 checkboxes where at least
one must be selected, by preventing unchecking. Seems to me that it
would pollute the ViewModel on one hand, and also feel like there is
certain "knowledge" about the View on the other hand.
You can easily inherit ObservableCollection<T> to create this logic in a reusable manner. Then in your ViewModel just put some public SelectableCollection<T> MyItems {get;set;} where the selection / mutual exclusion, etc is handled by SelectableCollection and so on.
Bottom line: MVVM is all about reusability and encapsulation of functionality.

How can my design implement MVVM?

I have an issue trying to understand how to implement the MVVM pattern in my application. It's a small application and I will explain what it does.
My application creates a backup of files. The UI lets the user choose which folder they want to back up and where it should be backed up too. After making their selection they click the "start" button.
This then passes the folder source and folder destination to a class library (called backup.cs) which creates the back ups of all the files inside each folder. During this, a log (Log.cs) is created logging each stage and the state of each file it attempted to back up (complete, failed, other, etc). Now, the log is in memory only.
When the back up is complete I want a window to open (a view) which will display all the logs. It's at this point I can't understand how to use the MVVM pattern.
As it stands today, I pass my log (which holds the data in a hierarchical way) to my MainWindow's constructor and bind to the datacontext, using a treeview in my xaml I get the desired results. However, I now want to use MVVM.
My question is very similar to my previous question, as you can see the answer is to pass the log as a paramter to the ViewModel constructor. The issue is, I don't know how to do that and also display a window!
The only way (in my head) I can achieve this is by passing the Log as a parameter to a constructor of my View but this defeats the point of the MVVM. I could pass the parameter to my ViewModel's constructor (which would fit the MVVM pattern) but would that then mean I have to also create an instance of my View from my ViewModel constructor as well? Otherwise all I would do is create my ViewModel but have no way to display the results since the View isn't displayed.
I hope I have explained where I'm struggling clearly, can any one suggest a way forward please?
Most likely you 'll want the viewmodel to accept (and expose through a property) a collection such as a List<Log> -- typically this would be an ObservableCollection<Log>, but if the operation has already completed there is no real point in going that way. This is what you are describing as a possible solution.
To wire the viewmodel to the view, in essence you need to do this:
var viewModel = new LogsViewModel(...);
var view = new LogsView(); // no constructor parameters
view.DataContext = viewModel;
And finally you add view at some place of the application window's logical tree so that it gets displayed. MVVM frameworks automate this procedure, but you can also do it manually as simply as this.
Your view would then bind to the logs collection to display each log, perhaps using a DataTemplate:
<ItemsControl ItemsSource="{Binding Logs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- XAML to display each Log does here -->
<TextBlock Text="{Binding FileName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
As an example, if you wanted that LogView being shown based on a button click in you main View.
public override void ShowCommandExecute()
{
var popup = new LogsView
{
WindowStartupLocation = WindowStartupLocation.CenterScreen,
DataContext = new LogsViewViewModel();
};
popup.ShowDialog();
}

Why the view constructor is not called every time I create a new viewmodel?

I am using WPF with model-view-viewmodel pattern. I have a ResultsView and ResultsViewModel which are connected like this:
<DataTemplate DataType="{x:Type VM:ResultsViewModel}">
<VW:ResultsView/>
</DataTemplate>
In my main window I have some kind of paged setup where MainContent property stores the current page (ViewModel instance).
I create the page like this
MainContent = new ResultsViewModel();
ResultsView is based on UserControl, it also has a handler for Loaded event which does some UI initialization stuff.
Everything works fine while the user is navigating between different pages.
But if the user opens the ResultsView two times in a row, the ResultsView constructor is not called the second time, and also Loaded event is not called. It seems that now I have the same old ResultsView instance attached to the new ResultsViewModel()!
Why WPF does not create a new View each time I create a new ViewModel? Is there some way I can force WPF to discard the old view if the old viewmodel is destroyed?
<DataTemplate x:Shared="False" DataType="{x:Type VM:ResultsViewModel}">
<VW:ResultsView/>
</DataTemplate>
See Kent's answer for practical work-around for your issue.
That said, it's a good practice to only instantiate the View once, as there is overhead associated with constructing the Visual Tree and setting up all of the bindings. Generally the View/ViewModel should be designed so that you can swap out the underlying ViewModel without the View caring or even noticing (other than DataContext changing and therefore all binding values being re-evaluated.) If you currently have logic in your Loaded event that prepares for a specific ViewModel, consider registering for notification of when DataContext changes instead (see this example).

Using Microsoft (or other) ribbon with large project and MVVM

Our application is a large project with many modules and view. The main window has a ribbon in it, and we are looking for the best way to integrate the ribbon in the application.
I've created a service which modules a views can register to add ribbon items relevant for them, and, in addition, any main view instance can provide its own ribbon items relevant to the that instance. a RibbonItem is a small class which abstract the options of a ribbon item, and mainly have Title, Description, Command, UIType and ChildItems. The service is in charge to rebuild the ribbon when the main view changes.
A colleague of mine thinks this is bad MVVM, as users need to design their ribbon view in C# code and not in XAML, and he also say it would be hard in this way to make a group of items disabled or enabled at once, as each command of these items will need to update its CanExecute separately. Instead, he suggested to have a main Ribbon View and ViewModel files, where each developer that want to add a ribbon button for her module or view would need to add them in the View XAML and add a relevant command in the ViewModel. In addition, VisualStates will be used to determine what items will be displayed or enabled based on changes in the ViewModel (such as view change, or selection change). I really don't like this solution, mainly because all developers will have to put their modules knowledge in once big file.
Note that some items in the ribbon (e.g. Options, Exit) are common to the entire application, while some are relevant to a specific application domain and some are only relevant for a specific view.
Edit: I guess my main question is what is the recommended way to allow multiple development teams integrate on a single ribbon? Should we have a single RibbonView and single RibbonViewModel which will contain all of the possible items in a ribbon, and each team will add its items to these V/VM and also define the logic on when to show them (probably by using visual state)? Or do we allow every view, view-model or module register ribbon items (within their own C# code) against a service, and have the service then render the ribbon as needed when the active view changes with all items registered to that type? Or is there any better way to achieve this integration?
What do you think?
Do you have a better idea or an opinion about how to manage the single ribbon resource which is common to multiple developers?
Thanks,
splintor
I agree with Will's comment, your viewmodel should not care or know how it is being rendered or if the designers ever decide to change how it's rendered.
A ViewModel should only contain all required information for the presentation layer to render it.
So the ViewModel should have all the properties that the Ribbon bar needs bind to in order to function. Then you can use a Resources.xaml or some other strategy to present it.
Taking a shot in the dark I would try something like this for the ViewModels:
public interface IMenuViewModel : INotifyPropertyChanged
{
ICommand Command {get;}
string Title {get;}
string Description {get;}
UIType Type {get;}
IList<IMenuViewModel> ChildItems {get;}
}
I would then probably create an abstract class that provides implements INotifyPropertyChanged with a collection class the implements INotifyCollectionChanged to take care of the plumbing code.
I would then probably do something like this in the Resources.xaml
<DataTemplate DataType="{x:Type vm:IMenuViewModel}">
<StackPanel>
<Button Command="{Binding Command}" Content="{Binding Type}"/>
<ItemsControl ItemsSource="{Binding ChildItems}"/>
</StackPanel>
</DataTemplate>
to provide a default view for your viewmodels
and then all someone has to do to create an entry into your ribbon bar is
1) Implement IMenuViewModel
2) Optionally add another DataTemplate entry into your resources.xaml if they want their widget rendered differently like so:
<DataTemplate DataType="{x:Type vm:FooViewModel}">
<v:FooView />
</DataTemplate>
I hope I didn't dig to deep on how I would implement.
The main point is that a ViewModel should only expose properties required for the view to do it's job(which is rendering the ViewModel), not for the ViewModel to do the job or care how it's done.

Resources