DevExpress MVVM : Injected ViewModel is also created by XAML. - c#

I am totally new to WPF. I'm using DevExpress mvvm framework. I want to show a window from a viewmodel.
The code I am using is
public void NewEntity()
{
var factory = ViewModelSource.Factory((RemoteTable<AddressLocatorView> aRemoteTable) => new CreateEntityWizardViewModel(aRemoteTable));
CreateEntityWizardViewModel aVM = factory(fDataModule.DataAdapter.GetTable<AddressLocatorView>());
DialogService.ShowDialog(dialogCommands: null, title: "New Entity Wizard", viewModel: aVM);
}
This will open a form and set the viewmodel.
Though, in the form's constructor, I have
public CreateEntityWizard()
{
InitializeComponent();
}
Which, in turn, calls the public parameterless constructo of the EntityWizardViewModel. The EntityWizardViewModel is created twice, once by the factory method, and second by the InitializeComponent() which, I believe, does it by the means of the XAML :
DataContext="{dxmvvm:ViewModelSource Type={x:Type ViewModels:CreateEntityWizardViewModel}}"
What is the approach to pass a ViewModel to a form using the DialogService ?
Thank you

When you pass your view model to the DialogService.ShowDialog method, this view model is automatically used as data context for the associated view (your dialog content). Therefore, the best way to avoid duplicate view model creation is to remove the DataContext setter in xaml. To let the designer know your data context type, use the d:DataContext property:
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
d:DataContext="{dxmvvm:ViewModelSource ViewModel:RegistrationViewModel}"
This approach is used for RegistrationViewModel in the following example on the DevExpress website: https://www.devexpress.com/Support/Center/e/T145641.aspx

Related

How to instantiate a dialog and record user input while MVVM pattern is used to create dialog in WPF

I am a C++ developer and new to WPF and MVVM. please bear with me if I choose any wrong word to ask my question
I have my Main application in MFC/C++ which is passing some data to C# library(CLI is used as middle layer).
In C# library, there is a section of code where a dialog is opened , data is filled and user selection is notified to the calling object in below way -
public classA()
{
MyDialog dlg = new MyDialog(param1, param2, param3)
if(dlg.ShowDialog().GetValueOrDefault())
{
var name = dlg.name;
var roll = dlg.roll;
}
else
{
var name = string.Empty;
var roll = string.Empty;
}
}
Now Dialog has been modified and implemented using MVVM pattern.
I have created below files as part of implementation-
1
MyDialogView.Xaml
MyDialogView.xaml.cs
MyDialogViewModel.cs
MyDialogModel.cs
My question is, how to instantiate the new dialog now from my classA so that data is filled using the parameters passed to dialog in same way as previously it was doing and record user selection without loosing any data and safely closing the view.
Standard MVVM approach works like this (at least when using MVVM Light):
You have a VM layer, a Class Library.
You have a View layer, a WPF Controls Library or WPF Application.
View layer adds reference to VM layer. VM layer doesn't know anything about View.
You create a normal public class for your dialog's VM. Call it DialogVM or whatever. Make sure this class inherits from MVVM Light's built-in ViewModelBase. This will get you access to change notification methods provided by MVVM Light. Might look like this in your case:
public partial class DialogVM : ViewModelBase
{
private string _Name;
public string Name
{
get { return _Name; }
set { Set(ref _Name, value); }
}
private string _Roll;
public string Roll
{
get { return _Roll; }
set { Set(ref _Roll, value); }
}
}
VM layer has a global static class called ViewModelLocator. This class performs IoC/DI and provides public static properties to expose different VMs. (In your case your dialog's VM goes to the VM project and the ViewModelLocator looks something like this:
using System;
namespace VMLayer
{
public class ViewModelLocator
{
static ViewModelLocator()
{
SimpleIoc.Default.Register<DialogVM>(true);
}
public static DialogVM MyDialog => SimpleIoc.Default.GetInstance<DialogVM>();
}
}
Your dialog box (a Window) goes to View layer and uses this exposed property MyDialog to provide a DataContext for the dialog:
<Window x:Class="GlasshouseTestingShell.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:vm="clr-namespace:VMLayer;assembly=VMLayer"
DataContext="{x:Static vm:ViewModelLocator.MyDialog}"
d:DataContext="{d:DesignInstance Type=vm:DialogVM}">
</Window>
Look how cleanly we have created View layer's DataContext without writing a line of C# code in the View layer. This is also elegant in the sense that you get all design-time Intellisense in Binding expressions and elsewhere.
You now bind all your UI stuff (textboxes, buttons etc.) to the public properties and commands exposed by your dialog's VM. Still no lines in the code-behind. Might look like this in your case:
<TextBox Text="{Binding Name}" />
Rest of the stuff is in C++:
You add reference to your View and VM DLLs in your C++ project.
Create an object of your dialog. It will automatically instantiate its VM and perform binding. You call ShowDialog() to bring it to screen.
Use takes actions in the dialog and finally presses OK or Cancel.
You capture dialog result and then access your dialog object's DataContext property, which is an object of DialogVM class. You can access user-supplied values from therein as Binding has updated those properties for you in the VM.
I'm not sure I follow all of your requirements but this is roughly how I'd approach such a task:
Instantiate the view and viewmodel in class A.
Set whatever parameters you want on your viewmodel. Either as properties or via constructor injection.
Set the datacontext of the view to the viewmodel.
Everything you need to bind should then bind between them.
showdialog the view.
The user edits in the view and changes persist to the viewmodel properties.
They finish editing and you then work with the viewmodel properties. Maybe one of them is the model you mention. Maybe the model is instantiated by the viewmodel to get data or by classA if that is more convenient. In the latter case you probably have to pass that model to the viewmodel.
Bearing in mind the above.
Some rough code:
public class ClassA
{
MyDialogViewModel vm = new MyDialogViewModel { Name = "X", Roll = "Y" };
MyDialog dlg = new MyDialog();
dlg.ShowDialog();
var name = vm.Name;
var roll = vm.roll;
// Do something to persist your data as necessary. Either here or in a model within the viewmodel
}
Name and Roll presumably bind to some textboxes Text properties in the view or some such.
If it's as simple as obtaining two string values then I see no advantage to actually having a model at all. On the other hand, if processing is more involved then of course the viewmodel might instantiate a model.
MyDialogViewModel should implement inotifypropertychanged and anything you need to bind should be a public property. Not sure if you'll need propertychanged notification but always implement it. Optionally raise propertychanged from property setters.

AddEditView (window) using WPF : passing Id to model construtor before opening form

I am very new to WPF. I am trying to create Add/Edit form. I have also added Caliburn.Micro frameowrk for MVVM.
I have created one screen for Add or Edit. I do not know how to pass model Id to my ViewModel. How?
Currently my View (Window) works only for Add and not Edit.
AddOrderViewModel.cs
corresponding AddOrderView.xaml
The classes are like this:
public class OrderAddEditViewModel : Screen {
public OrderAddEditViewModel( ) {};
// I do not know if this is normal approach or not
public OrderAddEditViewModel(int orderId) { // Load Order from DB};
...
}
public partial class OrderAddEditView : Window ...
Now, when pressing button on Main form, I am opening OrderAddEditView
//This loads empty form for Add
// What if I have OrderId and want to load it, HOW?
var frm = new OrderAddEditView();
frm.Show();
There is some magic behind, and OrderAddEditView knows which view model to use (this is WPF + Caliburn.Micro).
However, currently I want to pass "OrderId" to my OrderAddEditViewModel's constructor (this will load data from DB) and open OrderAddEditView so that constructor with id would be invoked.
Since you are using Caliburn.Micro, there is a neat method who opens your ViewModel.
Its called ActivateItem.
To use it just write the following.
ActivateItem(new YourViewModel([some attributes]));
Update 2:
I almost forgot...
To use ActivateItem method, one of your ViewModels has to be the Conductor. I like to think about it like base of all views. That's mostly the first ViewModel who starts with your app (except login screen, if you have it).
read about it more here.
This is a very uggly solution:
You create a Constructor for OrderAddEditView class.
And pass the id for the DataContext in that constructor?
The ViewModel is the DataContext of the View...

How to manage multiple windows in MVVM

I am aware there are a couple of questions similar to this one, however I have not quite been able to find a definitive answer. I'm trying to dive in with MVVM, and keep things as pure as possible, but not sure how exactly to go about launching/closing windows while sticking to the pattern.
My original thinking was data bound commands to the ViewModel triggering code to start a new View, with the View's DataContext then set to it's ViewModel via XAML. But this violates pure MVVM I think...
After some googling/reading answers I came across the concept of a WindowManager (like in CaliburnMicro), now if I was to implement one of these in a vanilla MVVM project, does this go in with my ViewModels? or just in the core of my application? I'm currently separating out my project into a Model assembly/project, ViewModel assembly/project and View assembly/project. Should this go into a different, "Core" assembly?
Which leads on a bit to my next question (relates somewhat to the above), how do I launch my application from an MVVM point of view? Initially I would launch my MainView.xaml from App.xaml, and the DataContext in the XAML would attach the assigned ViewModel. If I add a WindowManager, is this the first thing that is launched by my Application? Do I do this from the code behind of App.xaml.cs?
Well it mainly depends on how your application looks like (i.e. how many windows opened at the same time, modal windows or not...etc).
A general recommendation I would give is to not try to do "pure" MVVM ; I often read things like "there should be ZERO code-behind"...etc., I disagree.
I'm currently separating out my project into a Model assembly/project,
ViewModel assembly/project and View assembly/project. Should this go
into a different, "Core" assembly?
Separating views and ViewModels into different assemblies is the best thing you can do to ensure you won't ever reference something related to the views in your viewModel. You'll be fine with this strong separation.
Separating Model from ViewModel using two different assemblies could be a good idea too, but it depends on what your model looks like. I personally like 3-tier architectures, so generally my model is the WCF client proxies and are indeed stored in their own assembly.
A "Core" assembly is always a good idea anyway (IMHO), but only to expose basic utility methods that can be used in all the layers of your application (such as basic extension methods....etc.).
Now for your questions about views (how to show them...etc), I would say do simple. Personally I like instantiating my ViewModels in the code-behind of my Views. I also often use events in my ViewModels so the associated view is notified it should open another view for example.
For example, the scenario you have a MainWindow that should shows a child window when the user click on a button:
// Main viewModel
public MainViewModel : ViewModelBase
{
...
// EventArgs<T> inherits from EventArgs and contains a EventArgsData property containing the T instance
public event EventHandler<EventArgs<MyPopupViewModel>> ConfirmationRequested;
...
// Called when ICommand is executed thanks to RelayCommands
public void DoSomething()
{
if (this.ConfirmationRequested != null)
{
var vm = new MyPopupViewModel
{
// Initializes property of "child" viewmodel depending
// on the current viewModel state
};
this.ConfirmationRequested(this, new EventArgs<MyPopupViewModel>(vm));
}
}
}
...
// Main View
public partial class MainWindow : Window
{
public public MainWindow()
{
this.InitializeComponent();
// Instantiates the viewModel here
this.ViewModel = new MainViewModel();
// Attaches event handlers
this.ViewModel.ConfirmationRequested += (sender, e) =>
{
// Shows the child Window here
// Pass the viewModel in the constructor of the Window
var myPopup = new PopupWindow(e.EventArgsData);
myPopup.Show();
};
}
public MainViewModel ViewModel { get; private set; }
}
// App.xaml, starts MainWindow by setting the StartupUri
<Application x:Class="XXX.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...
StartupUri="Views/MainWindow.xaml">

MVVM MEF WindowFormHost

I am currently trying to design an application that loads viewmodels through MEF imports.
So far so good, I navigate from viewmodel to viewmodel, having loaded each vm datatemplate through dictionaries.
Each time I navigate, I modify the content of the main contentPresenter in my Shell (MainWindow).
One of the viewmodel allows me to display a WindowFormHost for an activeX control (such as acrobat reader for example). Since WindowFormHost does not allow binding, I created the windowFormHost in the viewmodel and binded it to a ContentPresenter in the view.
And here is where it fails : when coming back to the same viewmodel, the view is created again... throwing a “Element is already the child of another element.” error.
How can I prevent that ? Should I unload WindowFormHost when view is reloaded ? Or Can I keep view instances so that I keep only one instance for each view and let data binding update controls ? (It looks better for memory consumption).
Thanks for your help !
[EDIT]
Loaded dictionary :
<DataTemplate x:Shared="False" DataType="{x:Type vm:DAVPDC3DVIAControlViewModel}">
<vw:MyUserControl />
</DataTemplate>
View :
<DockPanel>
<ContentControl Name="WFH3DVia" Content="{Binding Path=Control3DVIA, Mode=OneWay} </ContentControl>"
<!--<WindowsFormsHost Name="WFH3DVia"></WindowsFormsHost>-->
</DockPanel>
VM (singleton, mef module) :
[Export(typeof(IDAVPDC3DVIAControl))]
public partial class DAVPDC3DVIAControlViewModel : ViewModelBase, IViewModel, IPartImportsSatisfiedNotification
VM (main window)
[Export]
public class MainWindowViewModel : ViewModelBase, IPartImportsSatisfiedNotification
// CurrentUC binds main widow view to controller active viewmodel
public IViewModel CurrentUC
{
get
{
return myAddinManager.CurrentVM;
}
}
Main view :
Controler (displays module on event) :
private void ModuleReadyEventAction(string iModuleName)
{
if (null != this.Modules && this.Modules.Count() > 0)
{
foreach (var item in Modules)
{
IBaseModule ibasemodule = item as IBaseModule;
if (null != ibasemodule)
{
Type tp = ibasemodule.GetType();
if (0 == tp.Name.CompareTo(iModuleName))
{
CurrentVM = ibasemodule.GetViewModel();
break;
}
}
}
}
}
I'm also working on a project in WPF using Prism v4 and MVVM (except I'm using Unity). I also have at least two controls that I need to use which are Windows Forms controls that must be hosted in a WindowsFormsHost. Let me explain my thoughts on the process..
It seems to me, that you are trying to avoid any code in your View's code behind. That's the only reason I can think of that you are moving your WindowsFormsHost into your ViewModel. I think that this is fundamentally the wrong approach. The WindowsFormsHost exists for the reason of displaying a graphical Windows Forms control. Therefore, it belongs in the view!
Now, I understand the appeal of DataBindings. Trust me, I've wanted to able to DataBind many parts of my WindowForms control. Of course, to accept a WPF data binding the property must be a dependency property on a dependency object. The easiest solution, which is not unreasonable, is to simply add the code to configure your windows forms control in the code behind for your view. Adding your UI logic into your ViewModel is an actual violation of the MVVM design pattern, while adding code behind is not. (And in some cases is the best approach)
I've seen possible hacks to try to get around this limitation. Including using "proxies" which inject a databinding, or perhaps extending WindowsFormsHost and adding DependencyProperties which wrap a specific hosted control's properties, or writing classes using reflection and trying to throw in windows forms bindings. However, nothing I've seen can solve the problem completely. For example, my windows forms control can contain other graphical components, and those components would need to support binding as well.
The simplest approach is to simply synchronize your view with your viewmodel in your view's code behind. Your view model can keep the file or document that is open, filename, title, etc., but leave the display and display related controls up to the View.
Last, let me comment more directly on your question. I would need to see how you are registering your View and ViewModel with the MEF Container and how you are navigating to understand why you are receiving that error. It would seem to me that either your view or view model is getting created more than once, while the other is not. Are these registered as singleton types? Regardless, I stand by what I said about not including the WindowsFormsHost in your ViewModel.

In C#, how can I set DataContext on a View from the ViewModel?

I"m trying to wrap my head around MVVM. I understand a lot of it, but I'm having difficulty grasping one aspect: Setting DataContext.
I want to show an view with a particular object. The user doesn't get to decide what is visible, so I need to create the view in code. Then, I want to set the DataContext of the view to an object (for binding). I'm trying not to put code in the View for this, but View.LayoutRoot.DataContext isn't public.
What am I missing?
trying to avoid this:
public class View
{
public View(object dataContext)
{
InitializeComponent();
LayoutRoot.DataContext = dataContext;
}
}
with something like this:
public class ViewModel
{
...
public UIElement GetView()
{
UIElement *element = new View();
element.LayoutRoot.DataContext = element;
return element;
}
}
Don't forget that the View should know about the ViewModel, and not the other way around.
So in your case puting code in the ViewModel to create the view isn't the best way around.
Josh Smith's article on MVVM has a section on applying the View to the ViewModel. He recommends using WPF's DataTemplates to select your View in XAML.
If you use a XAML control or Window (which should be the case if you use MVVM), LayoutRoot (Grid by default) is public. In your example, you use just a plain class for View, so it is hard to tell what is going on.
Also, I second Cameron's opinion - nor View or ModelView should deal with assigning DataContext. It can be done in different ways (DataTemplate, dependency injection, special builder class, plain code) but normally it happens on the application level.

Categories