I'm using Visual Studio 2013's designer to create my User Control in WPF, and I'm using a MVVM approach.
I'm trying to find the best way to have "Design-Time" setup of my viewmodel so that I immediatly see the effect in the designer of changing a value of a property for instance. I've used different designs and techniques to support this, but nothing is exactly what I want. I'm wondering if someone has better ideas...
Situation (simplified):
So I have a "Device" which I want a UserControl to show states and operations. From top to bottom:
I have a IDeviceModel which has a field bool IsConnected {get;} (and proper notification of state changes)
I have a FakeDeviceModel which implements IDeviceModel, and thus enables me to not rely on a real device for design-time and testing
A DeviceViewModel, which contains a IDeviceModel, and encapsulate the model's properties. (yes it has proper INotifyPropertyChanged notifications in it)
My UserControl which will have a DataContext of type DeviceViewModel, and would have a custom-styled CheckBox which is IsChecked={Binding IsConnected, Mode=OneWay
My Goal: I want to preview on design time how does the Model's IsConnected state affect my UserControl (So it could affect other things than just IsChecked)
Framework:
I use the idea of the MVVM Light ViewModelLocator, returning non-static fields (so new instances of ViewModels). At runtime, the real datacontext will be given by the one instanciating this UserControl
d:DataContext="{Binding DeviceViewModelDesignTime, Source={StaticResource ViewModelLocator}}"
public class ViewModelLocator
{
private static MainWindowViewModel _mainWindowViewModel;
public MainWindowViewModel MainWindowViewModelMainInstance
{
get
{
if (_mainWindowViewModel == null)
{
_mainWindowViewModel = new MainWindowViewModel();
}
return _mainWindowViewModel;
}
}
public DeviceViewModel DeviceViewModelDesignTime
{
get
{
//Custom initialization of the dependencies here
//Could be to create a FakeDeviceModel and assign to constructor
var deviceViewModel = new DeviceViewModel();
//Custom setup of the ViewModel possible here
//Could be: deviceViewModel.Model = new FakeDeviceModel();
return deviceViewModel;
}
}
Solutions I tried:
Compile-Time solution
Simply code the setup of the ViewModel in the ViewModelLocator.
var deviceViewModel = new DeviceViewModel(fakeDeviceModel);
var fakeDeviceModel = new FakeDeviceModel();
fakeDeviceModel.IsConnected = true;
deviceViewModel.AddDevice(fakeDeviceModel);
Pros: Simple
Cons: That's longer iterations of always going to change the value in code, recompile, go back to designer view, wait for result
Instance in resources and kept static in ViewModelLocator
So I create an instance in XAML and I try to push it in the current ViewModel used by the designer. Not the cleanest way, but worked for a while in simple situation (yes there's some wierdness with the collection, but was with the idea that I could have multiple devices and a current one)
XAML:
<UserControl x:Class="Views.StepExecuteView"
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:DataContext="{Binding DeviceViewModelDesignTime, Source={StaticResource ViewModelLocator}}">
<UserControl.Resources>
<viewModels:DesignTimeDeviceManager x:Key="DesignTimeDeviceManager">
<viewModels:DesignTimeDeviceManager.DesignTimeDevices>
<device:FakeDeviceModel IsConnected="True"
IsBusy="False"
IsTrayOpen="True"
NumberOfChipSlots="4"
/>
</viewModels:DesignTimeDeviceManager.DesignTimeDevices>
[... CheckBox binding to datacontext and so on...]
And ViewModelLocator.cs:
public class ViewModelLocator
{
private static MainWindowViewModel _mainWindowViewModel;
public MainWindowViewModel MainWindowViewModelMainInstance
{
get
{
if (_mainWindowViewModel == null)
{
_mainWindowViewModel = new MainWindowViewModel();
}
return _mainWindowViewModel;
}
}
public static FakeDeviceModel DeviceModelToAddInDesignTime;
public DeviceViewModel DeviceViewModelDesignTime
{
get
{
var deviceViewModel = new DeviceViewModel();
if (DeviceModelToAddInDesignTime != null)
deviceViewModel.AddDevice(DeviceModelToAddInDesignTime );
return deviceViewModel;
}
}
}
public class DesignTimeDeviceManager
{
private ObservableCollection<FakeDeviceModel> _DesignTimeDevices;
public ObservableCollection<FakeDeviceModel> DesignTimeDevices
{
get { return _DesignTimeDevices; }
set
{
if (_DesignTimeDevices != value)
{
_DesignTimeDevices = value;
ViewModelLocator.DeviceModelToAddInDesignTime = value.FirstOrDefault();
}
}
}
}
Pros:
Worked super great on one project. the instance that I had in XAML, I could modify the booleans and I would get -immediate- feedback on how it affects my UserControl. So in the simple situation, the CheckBox's "Checked" state would change and I could modify my styling in real-time, without needing to recompile
Cons:
It stopped working in another project, and this by itself I couldn't find the reason why. But after recompiling and changing stuff, the designer would give me exceptions looking like "Cannot cast "FakeDeviceModel" to "FakeDeviceModel""!! My guess is that the Designer internally compiles and uses caches for those types (C:\Users\firstname.lastname\AppData\Local\Microsoft\VisualStudio\12.0\Designer\ShadowCache). And that in my solution, depending on the ordering of things, I was creating a "FakeDeviceModel" which was assigned to a static instances, and "later on", the next time the ViewModelLocator would be asked for a ViewModel, it would use that instance. However, if in the meantime he "recompiles" or uses a different cache, then it's not "exactly" the same type. So I had to kill the designer (XDescProc) and recompile for it to work, and then fail again a few minutes after. If someone can correct me on this it would be great.
Multi-Binding for d:DataContext and custom converter
The previous solution's problem was pointing me to the fact that the ViewModel and the FakeDeviceModel were created at different moment in time (giving the type/cast problem) and to solve it, I would need to create them at the same time
XAML:
<UserControl x:Class="MeltingControl.Views.DeviceTabView"
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:UserControl.DataContext>
<MultiBinding Converter="{StaticResource DeviceDataContextConverter}">
<Binding Path="DeviceViewModelDesignTime" Source="{StaticResource ViewModelLocator}" />
<Binding>
<Binding.Source>
<device:FakeDeviceModel IsConnected="False"
IsBusy="False"
IsTrayOpen="False"
SerialNumber="DesignTimeSerie"
/>
</Binding.Source>
</Binding>
</MultiBinding>
</d:UserControl.DataContext>
public class DeviceDataContextConverter: IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length == 0)
return null;
var vm = (DeviceViewModel)values[0];
if (values.Length >= 2)
{
var device = (IDeviceModel)values[1];
vm.AddDevice(device);
}
return vm;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Pros:
-Works super nice! When the binding for the DataContext asks for the ViewModel, I take advantage of the Converter to modify that ViewModel and inject my device before returning it
Cons:
We lose intelissense (with ReSharper), since he doesn't know what type is returned by the converter
Any other ideas or modifications I could make to solve this issue?
You may create a design time ViewModel that returns IsConnected = true based on your view mode (FakeDeviceViewModel) and then set it as a design-time data context:
d:DataContext="{d:DesignInstance viewModels:FakeDeviceViewModel,
IsDesignTimeCreatable=True}"
Where viewModels: is the xaml namespace to the actual view model.
I've documented exactly how I managed to get the perfect setup to see live Design Time data in Visual Studio.
Look at Hint 9 - Design Time DataContext on this page:
ReSharper WPF error: "Cannot resolve symbol "MyVariable" due to unknown DataContext"
I would like to propose an alternative solution.
You could use that same view model for design time data and normal runtime, and check in your (single) viewmodel whether the designer is active and then load the design time data there.
In your view model you would do something like this:
public class ExampleViewModel : ViewModelBase
{
public ExampleViewModel()
{
if (IsInDesignMode == true)
{
LoadDesignTimeData();
}
}
private void LoadDesignTimeData()
{
// Load design time data here
}
}
The IsInDesignMode property could be placed in your view model base class - if you have one - and looks like this:
DesignerProperties.GetIsInDesignMode(new DependencyObject());
Please take a look at my answer here
This is how I am doing it one of my projects using MVVMLight.
Create interface for every viewmodel for which you need separate design time and run time properties and behavior.
Make separate viewmodels for every view - one for run time and another for design time. Derive both viewmodels from the same interface defined above.
Create a static class that has two static methods - one for registering services for run time in the IOC container and another for registering services for design time in the IOC container. I use the same SimpleIOC.Default container. Register appropriate viewmodels in both the methods bound to their interfaces.
public static class MyServiceLocator()
{
public static void RegisterRunTimeServices()
{
ServiceLocator.SetLocatorProvider(() => SimpleIOC.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<IAboutViewModel, AboutViewModel>();
}
public static void RegisterDesignTimeServices()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<IAboutViewModel, DesignTimeAboutViewModel>();
}
In constructor of ViewModelLocator, check if the app is in DesignMode and accordingly call the static method to register services.
public ViewModelLocator()
{
if (ViewModelBase.IsInDesignModeStatic)
{
MyServiceLocator.RegisterDesignTimeServices();
}
else MyServiceLocator.RegisterRunTimeServices();
}
Now, your Views just have to set datacontext as corresponding viewmodel interface instead of viewmodel object. To do that, instead of exposing viewmodel objects to each view from the ViewModelLocator, expose viewmodel interface.
In ViewModelLocator.cs
public IAboutViewModel About
{
get
{
return ServiceLocator.Current.GetInstance<IAboutViewModel>();
}
}
In AboutView.xaml
DataContext="{Binding Source={StaticResource Locator}, Path=About}"
Wherever needed in code, cast the interface to ViewModelBase type to convert it to ViewModel object and use.
In MainViewModel.cs
public class MainViewModel : ViewModelBase
{
private readonly IAboutViewModel _aboutViewModel;
public MainViewModel()
{
_aboutViewModel = ServiceLocator.Current.GetInstance<IAboutViewModel>();
CurrentViewModel = (ViewModelBase) _aboutViewModel;
}
}
So, basically I use DI to inject appropriate viewmodels to each view depending on whether the code is in run time or design time. The ViewModelLocator simply registers either design time or run time viewmodels in the SimpleIOC container. The benefit of this is that there is no mixing of code in viewmodel files and one can also setup the code for multiple design time data without much interference. If you want designtime data to show while the application runs, then also its possible with one line change in code.
I would create an instance of Fake View Model in a separate xaml e.g. DeviceViewModelDataSample.xaml (see example below)
Set Build Action to DesignData
Reference the file as such
<UserControl x:Class="YourNameSpace.YourControl"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DataContext="{d:DesignData Source=/DataSample/DeviceViewModelDataSample.xaml}">
<!-- Skiped details for brevity -->
</UserControl>
DeviceViewModelDataSample.xaml
<vm:DeviceViewModel xmlns:dm="clr-namespace:YourNameSpace.DataModel"
xmlns:vm="clr-namespace:YourNameSpace.ViewModel">
<vm:DeviceViewModel.DeviceManager> <!-- Assuming this is a collection -->
<dm:DeviceModel DeviceName="Fake Device" IsConnected ="true" /> <!-- This creates an instance at design time -->
</vm:DeviceViewModel.DeviceManager>
</vm:DeviceViewModel>
Related
I'm new to WPF and I'm writing a simple test app to familiarize myself with it. My test app will detect all joysticks I have attached to my computer and display information about it. So far, I have this ViewModel:
public class JoystickViewModel
{
public ObservableCollection<Joystick> Joysticks { get; set; }
public JoystickViewModel()
{
GetAttachedJoysticks();
}
private void GetAttachedJoysticks()
{
// populate Joysticks collection by using SlimDX
}
}
And this is my codebehind for my MainWindow.xaml:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new JoystickViewModel();
}
}
And my XAML for MainWindow:
<Window ...>
<Grid>
<ComboBox ItemsSource="{Binding Joysticks}"
DisplayMemberPath="Information.ProductName"/>
</Grid>
</Window>
I followed a tutorial that also populated the ViewModel in its constructor.
My question is, how should I populate the ViewModel? It seems sort of weird to me that I'm population the collection in the ViewModel constructor. Should this logic be in MainWindow's codebehind instead? Or somewhere else altogether? The end goal is to not only have this collection populated, but also updated periodically to reflect the current state (user plugged in new joystick, unplugged existing one, etc...).
The MainWindow code behind is definitively not the place where "business" logic should occur, as the View should be kept as simple as possible.
Keep your fetch/update logic inside of your viewmodel, this way you can test it easily and independently.
From a learning perspective, it's important to keep concerns separated :
the View is bound to the ViewModel, and has no intelligence
the ViewModel has knowledge on how to get the Model
the Model represents the data
In your case, the VM knowledge is at the moment a call inside it's constructor. Later you can change this to call some IJoystickDataService interface, and wire everything using a MVVM framework.
I would have your JoySticks observable collection property (and the code that populates it) in a Model class. The viewmodel simply exposes this same property to the view for binding. The vm should be as thin as possible - ideally just exposing properties that are in the model for binding and not doing any kind of 'business' logic (i.e. populating joystick info as in your case).
I understand that ViewModel shouldn't have any knowledge of View, but how can I call MediaElement.Play() method from ViewModel, other than having a reference to View (or directly to MediaElement) in ViewModel?
Other (linked) question: how can I manage View's controls visibility from ViewModel without violating MVVM pattern?
1) Do not call Play() from the view model. Raise an event in the view model instead (for instance PlayRequested) and listen to this event in the view:
view model:
public event EventHandler PlayRequested;
...
if (this.PlayRequested != null)
{
this.PlayRequested(this, EventArgs.Empty);
}
view:
ViewModel vm = new ViewModel();
this.DataContext = vm;
vm.PlayRequested += (sender, e) =>
{
this.myMediaElement.Play();
};
2) You can expose in the view model a public boolean property, and bind the Visibility property of your controls to this property. As Visibility is of type Visibility and not bool, you'll have to use a converter.
You can find a basic implementation of such a converter here.
This related question might help you too.
For all the late-comers,
There are many ways to achieve the same result and it really depends on how you would like to implement yours, as long as your code is not difficult to maintain, I do believe it's ok to break the MVVM pattern under certain cases.
But having said that, I also believe there is always way to do this within the pattern, and the following is one of them just in case if anyone would like to know what other alternatives are available.
The Tasks:
we don't want to have direct reference from the ViewModel to any UI elements, i.e. the the MediaElement and the View itself.
we want to use Command to do the magic here
The Solution:
In short, we are going to introduce an interface between the View and the ViewModel to break the dependecy, and the View will be implementing the interface and be responsible for the direct controlling of the MediaElement while leaving the ViewModel talking only to the interface, which can be swapped with other implementation for testing purposes if needed, and here comes the long version:
Introduce an interface called IMediaService as below:
public interface IMediaService
{
void Play();
void Pause();
void Stop();
void Rewind();
void FastForward();
}
Implement the IMediaService in the View:
public partial class DemoView : UserControl, IMediaService
{
public DemoView()
{
InitializeComponent();
}
void IMediaService.FastForward()
{
this.MediaPlayer.Position += TimeSpan.FromSeconds(10);
}
void IMediaService.Pause()
{
this.MediaPlayer.Pause();
}
void IMediaService.Play()
{
this.MediaPlayer.Play();
}
void IMediaService.Rewind()
{
this.MediaPlayer.Position -= TimeSpan.FromSeconds(10);
}
void IMediaService.Stop()
{
this.MediaPlayer.Stop();
}
}
we then do few things in the DemoView.XAML:
Give the MediaElement a name so the code behind can access it like above:
<MediaElement Source="{Binding CurrentMedia}" x:Name="MediaPlayer"/>
Give the view a name so we can pass it as a parameter, and
import the interactivity namespace for later use (some default namespaces are omitted for simplicity reason):
<UserControl x:Class="Test.DemoView"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ia="http://schemas.microsoft.com/expression/2010/interactivity"
x:Name="MediaService">
Hookup the Loaded event through Trigger to pass the view itself to the view model through a Command
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding ElementName=MediaService}"></ia:InvokeCommandAction>
</ia:EventTrigger>
</ia:Interaction.Triggers>
last but not least, we need to hookup the media controls through Commands:
<Button Command="{Binding PlayCommand}" Content="Play"></Button>
<Button Command="{Binding PauseCommand}" Content="Pause"></Button>
<Button Command="{Binding StopCommand}" Content="Stop"></Button>
<Button Command="{Binding RewindCommand}" Content="Rewind"></Button>
<Button Command="{Binding FastForwardCommand}" Content="FastForward"></Button>
We now can catch everything in the ViewModel (I'm using prism's DelegateCommand here):
public class AboutUsViewModel : SkinTalkViewModelBase, IConfirmNavigationRequest
{
public IMediaService {get; private set;}
private DelegateCommand<IMediaService> loadedCommand;
public DelegateCommand<IMediaService> LoadedCommand
{
get
{
if (this.loadedCommand == null)
{
this.loadedCommand = new DelegateCommand<IMediaService>((mediaService) =>
{
this.MediaService = mediaService;
});
}
return loadedCommand;
}
}
private DelegateCommand playCommand;
public DelegateCommand PlayCommand
{
get
{
if (this.playCommand == null)
{
this.playCommand = new DelegateCommand(() =>
{
this.MediaService.Play();
});
}
return playCommand;
}
}
.
. // other commands are not listed, but you get the idea
.
}
Side note: I use Prism's Auto Wiring feature to link up the View and ViewModel. So at the View's code behind file there is no DataContext assignment code, and I prefer to keep it that way, and hence I chose to use purely Commands to achieve this result.
I use media element to play sounds in UI whenever an event occurs in the application. The view model handling this, was created with a Source property of type Uri (with notify property changed, but you already know you need that to notify UI).
All you have to do whenever source changes (and this is up to you), is to set the source property to null (this is why Source property should be Uri and not string, MediaElement will naturally throw exception, NotSupportedException I think), then set it to whatever URI you want.
Probably, the most important aspect of this tip is that you have to set MediaElement's property LoadedBehaviour to Play in XAML of your view. Hopefully no code behind is needed for what you want to achieve.
The trick is extremely simple so I won't post a complete example. The view model's play function should look like this:
private void PlaySomething(string fileUri)
{
if (string.IsNullOrWhiteSpace(fileUri))
return;
// HACK for MediaElement: to force it to play a new source, set source to null then put the real source URI.
this.Source = null;
this.Source = new Uri(fileUri);
}
Here is the Source property, nothing special about it:
#region Source property
/// <summary>
/// Stores Source value.
/// </summary>
private Uri _Source = null;
/// <summary>
/// Gets or sets file URI to play.
/// </summary>
public Uri Source
{
get { return this._Source; }
private set
{
if (this._Source != value)
{
this._Source = value;
this.RaisePropertyChanged("Source");
}
}
}
#endregion Source property
As for Visibility, and stuff like this, you can use converters (e.g. from bool to visibility, which you can find on CodePlex for WPF, SL, WP7,8) and bind your control's property to that of the view model's (e.g. IsVisible). This way, you control parts of you view's aspect. Or you can just have Visibility property typed System.Windows.Visibility on your view model (I don't see any pattern breach here). Really, it's not that uncommon.
Good luck,
Andrei
P.S. I have to mention that .NET 4.5 is the version where I tested this, but I think it should work on other versions as well.
The application I'm currently writing is using MVVM with the ViewModel-first pattern. I have XAML similar to the following:
<ContentControl Content="{Binding FooViewModel.BarViewModel.View, Mode=OneWay}"/>
Every VM is a DependencyObject. Every property is a DependencyProperty. Depending upon the state of the application, the value of the BarViewModel property of the FooViewModel can change, thus changing the value of the View property. Unfortunately when this happens, the new view is not displayed, and the old one remains.
This is extremely frustrating. I thought that if any part of a path expression changed, the binding would update, but that doesn't appear to be the case. When I've used shallower path expressions, such as FooViewModel.View and I've changed the value of the FooViewModel property, that has updated the ContentControl to which it's bound, but not in this case.
If your solution is that I abandon ViewModel-first, that is not an option, though I appreciate your advice. I must get this working as is.
CLARIFICATION
This is a question about data binding, and not about MVVM or how to implement it. You can safely ignore the MVVM aspects of this if it helps you to think about the problem, or if you have a different idea about how MVVM should be implemented. This is a large, existing project in which the MVVM design pattern cannot be changed. (It is far too late for that.)
So, with that said, the correct question to be answering is the following:
Given a binding path expression in which every element is a DependencyProperty and the final property is a view bound to a ContentControl, why does a change in a property in the middle of the path not cause the binding to update?
Although I would expect this to work, there are several problems with your approach.
Firstly, your view models should not use DependencyObject or DependencyProperty, this ties them in to WPF. They should instead implement INotifyPropertyChanged. This makes your view models reusable in other presentation technologies such as Silverlight.
Secondly, your view models shouldn't have references to your views, so you shouldn't require a View property on your view models.
I would seriously consider using an MVVM framework for view composition - Caliburn.Micro, for example, makes view model first development extremely straightforward, and already provides a view model base class which implements INotifyPropertyChanged, and a mechanism for building view compositions with conventions.
I.e. you can have a conductor view model which has an ActiveItem property, and you simply place a ContentControl on your view with the same name as the property:
<ContentControl x:Name="ActiveItem" />
You can use the ActivateItem() method to change the current active item.
Caliburn.Micro also has a host of other features, such as being able to place a Button control with x:Name="Save" on your view, and your Save method on your view model will automatically be invoked when the button is clicked.
Every VM is a DependencyObject. Every property is a
DependencyProperty.
why? a viewmodel should be a simple class with INotifyPropertyChanged and the Properties should be simple properties.
and if you want your different viewmodel be rendered in a different way - you should use DataTemplate.
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyViewModelA}>
<MyViewA/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:MyViewModelB}>
<MyViewB/>
</DataTemplate>
</Windows.Resources>
<Grid>
<ContentControl Content="{Binding MyActualVM}"/>
</Grid>
</Window>
EDIT: btw you always bind to the last Property: FooViewModel.BarViewModel.View --> so the INotifyPropertyChanged (if raised) just work for the .View
EDIT2: another approach could be to get the BindingExpression of your content control and call.
System.Windows.Data.BindingExpression expr = //get it from your contentcontrol
expr.UpdateTarget();
EDIT3: and a simple mvvm way - just use INotifyPropertyChanged
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MyFooVM = new FooVM();
this.MyFooVM.MyBarVM = new BarVM(){View = "erster"};
this.DataContext = this;
}
public FooVM MyFooVM { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
this.MyFooVM.MyBarVM = new BarVM(){View = "zweiter"};
}
}
public class INPC : INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChanged(string property)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(property));
}
#endregion
}
public class FooVM:INPC
{
private BarVM _myBarVm;
public BarVM MyBarVM
{
get { return _myBarVm; }
set { _myBarVm = value;OnPropChanged("MyBarVM"); }
}
}
public class BarVM : INPC
{
private string _view;
public string View
{
get { return _view; }
set { _view = value;OnPropChanged("View"); }
}
}
I put the instance of one of the VMs in a resource dictionary like:
<ObjectDataProvider ObjectType="{x:Type WpfApplication1:MyViewModel}" x:Key="TheViewModel"/>
I bind the DataContext of some user controls to this:
<WpfApplication1:UserControl1 x:Name="UsrCtrl1" DataContext="{StaticResource TheViewModel}"/>
and it works fine at the runtime, because all connections and servers are available and a lot of logical objects are correctly initialized.
The problem is, in the design time I get a lot of exceptions (there are many such VMs), that make the work with very difficult.
Is it possible somehow to say in XAML if ComponentModel:DesignerProperties.IsInDesignMode (xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=PresentationFramework") is true then x:null, otherwise create my VM WpfApplication1:MyViewModel ???
I tried a lot, but was unable to get the right solution, but I cannot believe this is not possible. For any idea (maybe a tested example) thanks in advance.
The way I've dealt with this in the past involves providing an interface for your viewmodels and having the views ask for their viewmodel from a viewmodel locator class. For example, you'd have the following viewmodels:
public interface IMainViewModel
{
double Foo { get; }
double Bar { get; }
}
public class RealMainViewModel : IMainViewModel
{
// implementation of IMainViewModel, this one does your data access
// and is used at run time
}
public class FakeMainViewModel : IMainViewModel
{
// implementation of IMainViewModel, this one is fake
// and is used at design time
}
The viewmodel locator would look like the following:
public class ViewModelLocator
{
public static IMainViewModel MainViewModel
{
get
{
if (Designer.IsDesignMode)
{
return new FakeMainViewModel();
}
else
{
return new RealMainViewModel();
}
}
}
}
Finally, you'll include a reference to ViewModelLocator in App.xaml:
<Application.Resources>
<ResourceDictionary>
<yourNamespace:ViewModelLocator x:Key="ViewModelLocator" />
</ResourceDictionary>
</Application.Resources>
This way, you can bind to the viewmodel property in ViewModelLocator and have your code do the work for injecting the real and fake viewmodel when appropriate:
<WpfApplication1:UserControl1 x:Name="UsrCtrl1" DataContext="{Binding Path=MainViewModel, Source={StaticResource ViewModelLocator}}"/>
I also found an article that provides another example. Note that I wrote this code on-the-fly in Notepad so I apologize if there are any typos.
I believe you can use the following in your UserControl1 tag to define a Design-Time DataContext
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DataContext="{x:Null}"
I haven't actually tested it since I usually don't use the designer window, but in theory it should work :)
I'm working on a project that has several WPF User Controls that inherit from an abstract base class (itself based on UserControl). These controls render just fine at runtime, but they don't render in the designer.
I gather that this happens because the designer attempts to create an instance of the xaml root element, in this case my base class, but it can't create an instance because it is abstract.
For the record, I know that there are "patterns & practices" type issues with having this type of control hierarchy in WPF, but refactoring the entire project is not an option currently.
My question is this: I know that there are design time attributes for setting the DataContext, DesignWidth, etc. What I'm wondering is, can you give a "design time" instance or type to be provided as a replacement when the control is loaded in the designer?
in design time Visual Studio will try to create new Instant of YourUserControl
with parameterless constructor.
if you can't create usercontrol instant like this
var myView = new MyUserControl(); //no params
the designer will fail to render.
if YourUserControl required any parameter. the most popular trick is to create dedicate constructor like this
public MyUserControl() :
this(new MockViewModel(), new MockDataContext){ } // mock Designtime constructor
puclic MyUserControl(IViewModel vm, IDataContext context) //runtime constructor
{
}
in MVVM pattern some UserControl.DataContext is user-defined Type that required some params
XAML
<UserControl.DataContext>
<local:MyViewModel />
</UserControl.DataContext>
You must define parameterless constructor for design-time environment.
public MyViewModel() : this(new MockEventAggregator()) //for designtime
{ }
[ImportingConstructor]
public MyViewModel(IEventAggregator eventAggregator) //for runtime
{
this._eventAggregator = eventAggregator;
//...
}