I have just started using WPF with MVVM pattern. I had gone through some material related to MVVM.
However, the project I have to work on has an implementation of MVVM that seems very different than what I have read (maybe incorrect as well, not sure).
The implementation has all the Views (controls or windows) implemented as ResourceDictionary where all the controls, in the view are in the "Style" element.
The code behind for such ResourceDictionary have all the DependencyProperty and the Commands (there is no other class for ViewModel). Also, the classes (code behind) some how inherit from the Windows.Controls.Control class.
Is this the correct implementation ? If not what are the reasons that you see that prove this as a wrong implementation.
I may be wrong but the reasons I see are the following:
Implementing views as ResourceDictionary is not correct and Resources are not for creating custom views.
Having minimal code in the code behind is one of the important aspects of MVVM, that allows for loosely coupled architecture.
Since all views inherit from Windows.Controls.Control, writing unit test cases for the views would be difficult.
Am I correct or there are some other reasons that this implementation is incorrect (or am I wrong and this can be a way to implement MVVM in WPF).
Your views are highly appreciated.
Below is a sample code: (XAML)
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Presentation"
>
<Style TargetType="{x:Type local:FirstControl}">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:FirstControl}">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Height="490" DataContext="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=OneTime}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="TEST TEXT" FontWeight="DemiBold"/>
<Button Command="{Binding Path=CloseCommand, Mode=OneTime}"
Width="48" Height="30"/>
</StackPanel>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Code Behind:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace Presentation
{
/// <summary>
/// View-model
/// </summary>
public class FirstControl : Control
{
static FirstControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FirstControl), new FrameworkPropertyMetadata(typeof(FirstControl)));
}
public FirstControl()
{
CloseCommand = new DelegateCommand(OnCloseCommand);
}
private void OnCloseCommand()
{
// Write code to close application.
}
public static readonly DependencyProperty CloseCommandProperty = DependencyProperty.Register("CloseCommand", typeof(ICommand), typeof(FirstControl));
public ICommand CloseCommand
{
get { return (ICommand)GetValue(CloseCommandProperty); }
set { SetValue(CloseCommandProperty, value); }
}
}
}
Hope this helps.
The DelegateCommand is a class to allow delegating command logic to methods passed as parameters.
The main point of MVVM is to allow each layer to be fully tested without the need of "higher" layers.
You should be able to test the Model, and in that test you should be able to successfully complete all the tasks required to send and retrieve data from your data store. Your model testing should not require any view or view-model to complete.
You should be able to test your View Model without the need for any UI code or other View level code. Your View Model should be able to logically do everything your application needs to do without any user interraction or UI code. Ideally, you should be able to test your ViewModel using mocked Model classes that provide predictable responses.
Related
I can't figure this one out. I have a WPF application using the MVVM pattern with Unity constructor dependency injection. In the application, I use a custom control. All was well at first: I added the control to my main window and it displayed in the VS designer just fine. Then I wanted the control to do something useful, and to do so, it needed a data provider. I decided that the best way to provide that was to add the provider as a dependency in the constructor.
That's when everything went south. Although the program runs as expected, the VS designer can't instantiate the control. I've built a simple application to illustrate my dilemma.
MainWindow code behind:
using System.Windows;
using System.Windows.Controls;
using Microsoft.Practices.Unity;
namespace DependencyInjectionDesigner
{
public interface IDependency { }
class Dependency : IDependency { }
class DependentControl : Control
{
public DependentControl()
: this(App.Unity.Resolve<IDependency>()) { }
public DependentControl(IDependency dependency) { }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
MainWindow XAML:
<Window x:Class="DependencyInjectionDesigner.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DependencyInjectionDesigner"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="{x:Type local:DependentControl}">
<Setter Property="Margin" Value="30"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:DependentControl}">
<Border BorderBrush="Green" Background="Gainsboro"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<local:DependentControl/>
</Grid>
</Window>
App code behind:
using System.Windows;
using Microsoft.Practices.Unity;
namespace DependencyInjectionDesigner
{
public partial class App : Application
{
public static IUnityContainer Unity { get; private set; }
protected override void OnStartup(StartupEventArgs e)
{
if (Unity != null) return;
Unity = new UnityContainer();
Unity.RegisterType<IDependency, Dependency>(
new ContainerControlledLifetimeManager());
}
}
}
I think the problem is that the VS designer doesn't know to register the IDependency type before newing up the control. Am I correct? Is there way around this?
I'm using VS 2010 Ultimate and .Net 4.0.
The VS designer will try to call a zero-argument constructor using new to create the control in the designer; it knows nothing about and will not try to resolve through your container. In addition, your App.Unity property is not available to the designer, nor has setup code run.
Your best best to to change your control's constructor to use a stubbed out design time only data provider instead of trying to resolve through the container when using that constructor.
I was making a UserControl when I found this strange phenomena. If I use C# code to place a GroupBox in the UserControl's template and then make any SetResourceReference call on the GroupBox, suddenly the GroupBox inherits the foreground of TemplateParent (my UserControl).
So far I have found the following requirements for this situation:
UserControl base type does not matter
Affected template child must be a GroupBox (but not necessarily the first template child)
The foreground of the GroupBox may be explicitly set in the template, overriding the inherit
Must be using some sort of reference call from the GroupBox
Only the Foreground property seems to be affected
Here is my sample code:
MainWindow.xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="350">
<Window.Resources>
<Thickness x:Key="TestPadding">5</Thickness>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="Foreground" Value="Red" />
<Setter Property="Background" Value="Orange" />
</Style>
</Window.Resources>
<Grid>
<my:TestControl Foreground="Blue" Background="Purple" />
</Grid>
</Window>
TestControl.cs:
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Markup;
namespace WpfApplication1
{
public class TestControl : UserControl
{
public TestControl()
{
FrameworkElementFactory group = new FrameworkElementFactory(typeof(GroupBox));
group.SetValue(GroupBox.ContentProperty, "My Child");
group.SetResourceReference(GroupBox.MarginProperty, "TestPadding");
this.SetValue(TestControl.TemplateProperty, new ControlTemplate(typeof(TestControl)) { VisualTree = group });
}
}
}
What do you guys think, is this a bug that I should report to Microsoft?
I don't think that is a Microsoft issue. In fact i think it works fine. You are defining a Templeate in code behinde for your TestControl, and you are setting a GroupBox as root element for the template. What happens here is that your UserControl.Foreground property is not the same that the GroupBox that is the root of your template, then the GroupBox (as GroupBox) will take the Foreground that inherits from the Resources (in this case Window's Resources).
If you want to solve this, you can do something like "TemplateBindings", the follow code will works for you like a TemplateBinding:
namespace WpfApplication1
{
public class TestControl : UserControl
{
public TestControl()
{
FrameworkElementFactory group = new FrameworkElementFactory(typeof(GroupBox));
group.SetValue(GroupBox.ContentProperty, "My Child");
group.SetResourceReference(GroupBox.MarginProperty, "TestPadding");
//This line will work as a TeplateBinding
group.SetBinding(GroupBox.ForegroundProperty, new Binding() { Path = new PropertyPath("Foreground"), RelativeSource = RelativeSource.TemplatedParent });
this.SetValue(TestControl.TemplateProperty, new ControlTemplate(typeof(TestControl)) { VisualTree = group });
}
}
}
Hope this answer will be useful for you.
I have contacted the Microsoft WPF Development Team. They acknowledge this as a bug, but have prioritised it as low and not likely to be fixed.
My work around for this example:
Use another control to take the *.SetResourceReference call to perform padding, instead of the GroupBox.
We have been using the DataContextProxy concept based on (or exactly) as described in Dan Wahlin's Blog. Functionally This has worked fine for our purposes. However, after doing extensive memory profiling, and after discovering similar reports online (link below), it seems that this approach leaks memory as a result of a problem/bug with UserControl.Resources.
Has anyone found a decent alternative to the DataContextProxy approach?
Connect Report, which says the problem has been fixed in SL 5. I am going to try to post a repro solution for SL4.
I've come up with something very close to the DataContextProxy but instead of the binding being created in the Loaded event of the class, the declaration in the XAML binds back to the class. Seems to work exactly the same except it doesn't leak.
Would love someone else to verify this.
<UserControl.Resources>
<local:DataContextProxy x:Key="DataContextProxy" ViewModel="{Binding Path=DataContext, ElementName=LayoutRoot, Mode=TwoWay}" />
</UserControl.Resources>
The class
namespace Silverlight.Infrastructure
{
/// <summary>
/// Refactored to not leak. Set binding on ViewModel propery to DataContext of page, in Resources of page
/// Binding in XAML on declaration of DataContextProxy
/// Usage: <shared:DataContextProxy x:Key="DataContextProxy" ViewModel="{Binding Path=DataContext, RelativeSource={RelativeSource Self}, Mode=TwoWay}" />
/// </summary>
/// <remarks></remarks>
public class DataContextProxy : DependencyObject
{
public static DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof (object), typeof (DataContextProxy), new PropertyMetadata(default(object)));
public object ViewModel
{
get { return (object)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
}
}
usage:
DataToBindTo="{Binding ViewModel.DataToBindTo, Source={StaticResource DataContextProxy}}"
Changed the binding to ElementName as it seemed to work better on child views that didn't get a viewmodel until after the binding had been initially resolved.
You can look at this SL implementation of a relative source binding. Caliburn Micro also has a way to solve this problem by Action bubbling.
I'm not aware of memory leaks problems, but as Silverlight 4 introduced ElementName in bindings, it mostly eliminates the need for a DataContextProxy:
<ListBox ItemsSource="{Binding DataContext.Languages, ElementName=LayoutRoot}">
More explanations here.
Does anyone know why I can't set an event on a control template??
For example, the following line of code will not compile. It does this with any events in a control template.
<ControlTemplate x:Key="DefaultTemplate" TargetType="ContentControl">
<StackPanel Loaded="StackPanel_Loaded">
</StackPanel>
</ControlTemplate>
I am using a MVVM design pattern and the control here is located in a ResourceDictionary that is added to the application's MergedDictionaries.
Does anyone know why I can't set an event on a control template??
Actually, you can... But where would you expect the event handler to be defined ? The ResourceDictionary has no code-behind, so there is no place to put the event handler code. You can, however, create a class for your resource dictionary, and associate it with the x:Class attribute :
<ResourceDictionary x:Class="MyNamespace.MyClass"
xmlns=...>
<ControlTemplate x:Key="DefaultTemplate" TargetType="ContentControl">
<StackPanel Loaded="StackPanel_Loaded">
</StackPanel>
</ControlTemplate>
C# code :
namespace MyNamespace
{
public partial class MyClass : ResourceDictionary
{
void StackPanel_Loaded(object sender, RoutedEventArgs e)
{
...
}
}
}
(you might also need to change the build action of the resource dictionary to "Page", I don't remember exactly...)
Having read all the StackOverflow entries regarding Model-View-ViewModel architecture along with most of the readily available resources on the net I have come to the conclusion that it is the de-facto standard for building SOLID Silverlight apps.
I started to plan my next application using this architecture. One of the requirements for the application is to build or change the visual component structure. For example, responding to user interaction I would like to programmatically append an unknown-at-compile-time number of Rectangles and Ellipses to a specific Canvas or Canvases.
I started to twist my mind to apply the MVVM pattern and think where the code responsible for attaching the new components should go. I concluded that it doesn't belong to any of the MVVM layers and therefore it is impossible to apply this architecture in scenarious where you have to manipulate the component tree at runtime.
Is there a way to still use Model-View-ViewModel in these kind of scenarious or it is only limited to work with a fixed View component structure?
Don't manipulate the component tree. Instead, manipulate a model that represents the component tree. Then have your view bind to the various collections and properties in that model to produce its visuals.
What follows is a really simplified example. It just shows the concepts - please don't take it as indicative of how you should factor your code.
First, my model:
public abstract class Shape
{
public double Left { get; set; }
public double Top { get; set; }
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
}
Next, I expose a collection of said shapes (you would use another model to contain this collection). Then I bind to it in my view:
<Window x:Name="_root" x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Rectangle}">
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Stroke="Black"/>
</DataTemplate>
</Window.Resources>
<ItemsControl DataContext="{Binding ElementName=_root}" ItemsSource="{Binding Shapes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Left}"/>
<Setter Property="Canvas.Top" Value="{Binding Top}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Window>
The fundamental problem in your question is confusing requirements of your users (manipulating objects that are represented by rectangles and ellipses (I'm only guessing)) with implementation details (appending Rectangles and Ellipses to Canvases).
Again, the different responsibilities in the MVVM pattern:
View
Translate the ViewModel into pixels and translate input events into method calls on the ViewModel.
This would be the actual Silverlight components (Rectangle, Ellipse, Canvas) binding against their DataContext and having a few very small event handlers or Commands or whatever.
Model
Hold data and business logic in a domain-specific way.
This represents the "mathematical" rectangles and ellipses your users are drawing.
ViewModel
Refine the Model in a UI-oriented and often use-case specific way.
Here you store the transient information like "currently selected object" that are relevant for a specific view but are not attributes of the underlying Model's concept.
Read my blog for more on my views on MVVM.