I am working with a WPF application and using my own architecture that strongly resembles a M-V-VM /MVC. I have controllers for each of the views, and I have ViewModels that are bound to the Views.
For Example, I have a ToolBarView that has a corresponding ToolBarViewModel, and ToolBar Controller.
I am using notifications to update all the views so that they do not need to reference each other, but that is not relevant for my question.
Each of the Views is listening for an event to trigger in their controller when the model has been updated. In the ToolBarView this looks like the following.
/*In Constructor in ToolbarView*/
Controller.Updated += UpdateView
/*Event Handler in ToolbarView*/
private void Updateview(object sender,EventArgs e)
{
DataContext = Controller.Model;
//Other Updating if needed
}
If not obvious, what the above code is doing is saying that when the Controller fires the Updated event, to invoke the UpdateView(object sender,EventArgs e).
The problem that I am experiencing is that the first time that the UpdateView() is invoked, everything is working fine, but when it is invoked the second time, the DataContext seems to still be bound to the original Controller.Model.
It seems almost as if I have to release the DataContext, or refresh it in order for it to be bound to the Model every time.
The Controller is performing operations on the Model, and therefor when the UpdateView() is invoked, it needs to display the newly assigned values on that model.
Is there some way I need to refresh the DataContext, or is there a different way I need to do this?
If you are assigning the DataContext to the same instance of your model, the in effect it won't "change". WPF expects objects to notify it when their state changes, either through DependencyProperty properties or by implementing INotifyPropertyChanged.
So if you do something like:
MyObject o = new MyObject();
o.MyString = "One";
this.DataContext = o;
// ... some time later ...
o.MyString = "Two";
this.DataContext = o;
Assuming MyObject doesn't implement INotifyPropertyChanged, then the second assignment to DataContext is effectively worthless. You would have to set DataContext to null, then assign your object again to have it "refresh".
But your best bet in general would be to implement INotifyPropertyChanged. This would end up being much more efficient, as only the property that actually change would need to be updated in the UI.
Related
I am building a large scale WPF app - kind of a desktop that hosts multiple modules such as multiple Terminal windows over RS232/Ethernet, Register analyzer, automation tools, etc.
I am working with MVVM architecture where my view (XAML) instantiate the corespondent viewmodel in its resource section. and the view model is set in the data-context of the view.
in this method the view created first.
however, I red about another method called VM first, meaning view model is instantiated before the view, and I understood the theory that stands behind it. What I didn't understand is when and who instantiate the view and how it happens without coupling to the view.
I'll be more than happy to hear your thoughts, and please if someone can supply code samples it would be great..
Thanks in advance.
I'm using MVVM heavily in my projects and can share my view on this.
In my projects the view never instantiates any VM. Usually I have some kind of manager which takes care that the corresponding VM is created.
This I'm assign to the datacontext of some top-level UI control (Window for instance). The view is always defined by a style where the target type is set to the type of the view model.
The startup code just creates a Window and the main viewmodel. The VM is assigned and the rest is done by the WPF (.net) runtime so to say.
So I have a large style file, where all the styles for each viewmodel defines the corresponding view (usually a usercontrol).
This is the way I'm doing things, there are for sure others too.
hth
In my WPF / MVVM applications I use ViewModels with two constructors - one for design time (no paramaters - mock version of required components are set directly) and another for runtime (required components are injected as parameters via IoC). This allows for (mock) data to be displayed inside the Visual Studio designer for UI testing purposes.
So the simple case looks like ...
public class MainViewModel : ViewModelBase
{
private IDataFactory _DataFactory;
public MainViewModel()
{
_DataFactory = new DesignTimeMockDataFactory();
LoadData();
}
[PreferredConstructor]
public MainViewModel(IDataFactory dataFactory)
{ _DataFactory = dataFactory; }
public void LoadData()
{ DataItems.AddRange(_DataFactory.GetDataItems()); }
public ExtendedObservableCollection<DataItem> DataItems { get; private set; }
}
The design time usage can be set directly in the XAML ...
<Window x:Class="MainView"
d:DataContext="{d:DesignInstance Type=MainViewModel, IsDesignTimeCreatable=True}"
...
The run-time ViewModel is set in the code behind of the View ...
public MainView()
{
InitializeComponent();
var viewModel = SimpleIoc.Default.GetInstance<MainViewModel>();
DataContext = viewModel;
Loaded += (s, e) => viewModel.LoadData();
}
The View's Loaded event is set to call the LoadData() method of the ViewModel to trigger data loading, once the View is displayed. If LoadData() is slow, it can be changed into an async method to prevent the UI from blocking.
For those of you complaining that this is a too tightly coupled construct, my view is that is exactly how they are supposed to be. Although the View and ViewModel are separate entities, each View knows exactly what type of ViewModel it requires, and that's unlikely to change over the project development life-cycle. Using a Locator type class to hide the ViewModel constructor calls is an unnecessary level of abstraction in my opinion.
To decouple the view from the view-model, something else needs to instantiate the view model and manage its lifetime and sharing. That job might fall to an IoC container, or simple, manual dependency injection. It's entirely up to you.
E.g. from Paul Stovell's article:
public CalculatorView(CalculatorViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
It all depends on you're trying to achieve by decoupling. One reason might be so that you can have multiple views over the same view-model - in that case, whatever creates the views needs to also create the view-model.
Another may be to swap the view-model of an existing view out with another view-model without destroying the view. In that case, maybe you already have two existing view-models and you assign them to the view's DataContext as required.
view.DataContext = viewModels[0];
view.DataContext = viewModels[1];
when your application grows you usually face these decisions. Usually you have "always" both elements together the View and the ViewModel it's not about what comes first it's more like what will you use to instantiate the two elements (view and viewmodel).
For larger projects, when I had the need, I used a class called ViewModelResolver. It obviously has an interface IViewModelResolverso it can be injected nicely.
It can either return a ViewModel based on convention based on type or a string representation and uses reflection to instantiated it.
You can also pass in a ViewModel (or type) and get the matching view with the passed in view model as DataContext (view ViewModel marriage) or you can define other custom scenarios that you need for instantiating either view or ViewModel.
hope that helps
So the main point is to have an intermediate class that acts like some sort of factory service that takes car of bringing views and view models together and instantiate them.
This gives you more freedom and a good place to separate out those decisions from the ViewModel directly.
So I'm working on a GUI and most of it I implemented with 1 window and used the code-behind for that window to handle most of the logic. The program is very GUI driven. Say there is a combo box, if you select something from the combo box, the data drastically changes and all the other GUI boxes/labels/grids change or clear ect ect.
I'm doing a lot of refactoring and I've been aware of MVVM, but I've never really seen the need for it. I understand what and why its used, but functionality its just easier to reference all the GUI components straight from the code behind I've found.
For example...
In my main window I have
<ComboBox x:Name="MyTitlesUI" ItemsSource="{Binding Titles}" SelectionChanged="MyTitlesUI_SelectionChanged">
So the ComboBox is tied to a List Titles in my MainWindowViewModel right?
Where should MyTitlesUI_SelectionChanged event go? It needs to go in the View correct? But what if the functionality of SelectionChanged has to do with data inside MainWindowViewModel?
Say you change the selection in MyTitlesUI and now the program has to look up up that Title string in a database. All of that database functionality is in DBClass which you declare in MainWindowViewModel. How do you access that functionality? Why would you have to do this:
In main window cs:
private void MyTitlesUI_SelectionChanged(object sender, EventArgs e)
{
viewModel.ConnectToDataBase((string)MyTitlesUI.SelectedItem);
}
In MainWindowViewModel.cs
private SelectedTitle;
public void ConnectToDataBase(string title)
{
SelectedTitle = title;
DBClass myDB = new DBClass(SelectedTitle);
.... //do stuff with myDB
}
That just seems kind of unnecessary no? This is just a mild mild example of course and maybe that seems pretty clean. But if you're doing really complex back and fourth between View and ViewModel, the reference to MyTitlesUI.SelectedItem in View may be needed in ViewModel for other functions to work hence the SelectedTitle private variable.
Now you have more assignments, more variables, more functions that just call other functions than just a simple MyTitlesUI.SelectedItem to deal with.
Why not bring the DBClass reference up to the View or similar?
Especially if you're doing a lot of UI manipulation that the information inside your ViewModel will be playing with. Say once I change the selection of Title, I need graph to clear. But my graph can't clear until my ViewModel has connected to the DB or something.
I'm going to have graphs or grids defined in my View that depend on dynamically created data in my ViewModel that needs to update. And I'm trying to wrap around what needs to be in View and what needs to be in ViewModel. It seems to be not proper to reference View from ViewModel, so something like MyTitlesUI.SelectedItem can't be called in ViewModel.
EDIT:
So going back to the Selected Item example, say I have a Treeview UI item. I want to bind that to a Treeview that I don't have yet. I create the data for it procedural with DB connect. So the user selects from the combobox the Title they want. Db Connect then creates, asynchronously, a TreeviewItem in some kind of data structure.
private SelectedTitle;
public void ConnectToDataBase(string title)
{
SelectedTitle = title;
DBClass myDB = new DBClass(SelectedTitle);
if(myDB.doneWorking)
{
myTreeView.ItemsSource = myDB.GetTree();
}
}
but functionality its just easier to reference all the GUI components
straight from the code behind I've found
Wrong. MVVM delivers a clean, Property-based approach that's much easier to work with than the txtPepe_TextChanged() winforms-like approach. Try to change the Text for a TextBlock buried deep inside a DataTemplate that is used as the ItemTemplate of a Virtualized ItemsControl using code behind... WPF is not winforms.
Where should MyTitlesUI_SelectionChanged event go?
Nowhere. MVVM works best with a property/DataBinding based approach, as opposed to a procedural event-based approach.
For instance, a ComboBox-based UI that "does stuff" when the user changes the selection in the ComboBox should be defined like this:
<ComboBox ItemsSource="{Binding MyCollection}"
SelectedItem="{Binding SelectedItem}"/>
ViewModel:
public class ViewModel
{
public ObservableCollection<MyItems> MyCollection {get;set;}
private void _selectedItem;
public MyItem SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
NotifyPropertyChanged();
DoStuffWhenComboIsChanged();
}
}
private void DoStuffWhenComboIsChanged()
{
//... Do stuff here
}
}
Now you have more assignments, more variables, more functions that
just call other functions than just a simple MyTitlesUI.SelectedItem
to deal with.
Wrong. What you have now is a Strongly Typed property in the ViewModel of type MyItem that you work with instead of the horrible casting stuff (MyItem)ComboBox.SelectedItem or things of that sort.
This approach has the additional advantage that your application logic is completely decoupled from the UI and thus you're free to do all sorts of crazy stuff on the UI (such as replacing the ComboBox for a 3D rotating pink elephant if you wanted to).
Why not bring the DBClass reference up to the View or similar?
Because DataBase code does NOT belong into the UI.
Especially if you're doing a lot of UI manipulation
You don't do "UI manipulation" in WPF. You do DataBinding which is a much cleaner and scalable approach.
I am having a problem with DomainContext.RejectChanges() and reflecting the rollback in the UI. Here is my scenario.
I have a Model (Entity) generated for use with RIA services (I'll call it Foo)
I have a ViewModel that wraps Foo and extends it (I'll call it FooViewModel)
I have a View that is using Binding to display and update data using the FooViewModel
I have an "outer" ViewModel that holds an ObservableCollection of FooViewModels
The "outer" View has a list box bound to the ObservableCollection
So essentially there is a listbox of FooViewModels on one screen...when you select an item a childwindow is displayed to edit that particular FooViewModel. The FooViewModel is serving both the listbox and the childwindow.
Editing works just fine. A change in the childwindow reflects in the listbox immediately because I am calling RaisePropertyChanged() when the viewmodel properties are updated.
However, If I perform a DomainContext.RejectChanges()...the underlying entity gets rolled back (all changes reverted as expected)...however the FooViewModel isn't aware that this change has occurred and thus the UI isn't updated. If I reselect the item in the listbox on the first screen, the childwindow is displayed with the rolled back changes (which is what I want). The listbox still isn't updated though.
When I reject changes, if I kludge a RaiseProperyChanged() for the field that I changed...the UI listbox does update.
How do I get the UI to update when the underlying entity is rejected?? And how do I do it without tracking what properties of the viewmodel were rolledback? There has to be an easy way to accomplish this that I am just missing.
Something you could try is use the PropertyChanged event on the underlying entity Foo to trigger a RaisePropertyChanged pass on the FooViewModel properties.
so making some assumptions (so this code make sense):
You have a private variables in your FooViewModel
private Foo _foo;
private DomainContext _context;
You have a method on your FooViewModel that is calling RejectChanges() on your domain context.
Like so:
public void RejectChanges()
{
_context.RejectChanges();
}
We have a method that raises the PropertyChanged event on our FooViewModel
Like so:
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(propertyName);
}
Ok, now we have that established, lets have a look at exactly what happens when you call RejectChanges() on a domain context.
When you call RejectChanges() this bubbles down through the DomainContext to its EntityContainer, then to each EntitySet in that container and then to each Entity in the set.
Once there (and in the EntitySet), it reapplies the original values if there was any, removes the entity if it was added, or adds it if it was deleted. If there was changes to the values, then it applies them back to the properties.
So theoretically, all the RaisePropertyChanged(), that are generated in the entity properties, should be triggered.
NOTE: I haven't actually tested this. If this isn't the case, then none of this works :P
So we can hook into PropertyChanged event of the Foo entity, and raise the PropertyChanged event on our FooViewModel.
so our RejectChanges() method might look like this:
public void RejectChanges()
{
Func<object, PropertyChangedEventArgs> handler = (sender, e) =>
{
RaisePropertyChanged(e.PropertyName);
};
_foo.PropertyChanged += handler;
_context.RejectChanges();
_foo.PropertyChanged -= handler;
}
So we hook up an event handler to our Foo entity, which calls the FooViewModel.RaisePropertyChanged method with the property name that is changing on the Foo entity.
Then we reject changes (which triggers the property changes),
then we unhook the event handler.
Pretty long winded, but I hope this helps :)
I presume that the call to DomainContext.RejectChanges() is happening within the ViewModel as you probably bound that to some command or method called from the parent ViewModel. Since all your bindings to the data is done on the ViewModel properties you will have to raise the property change event on them when you directly manipulate the model outside of those properties. Which you probably doing already.
public void RejectChanges()
{
DomainContext.RejectChanges();
RaisePropertyChangeOnAll();
}
How you implement RaisePropertyChangeOnAll() can be done simply with a list of RaisePropertyChange("...") for each property or you could do it through reflection (if Silverlight permissions allow, not too sure about it) by adding an Attribute on each property you want to raise. Find all the properties that are tagged with it and call RaisePropertyChanged on the MemberInfo.Name value.
[Raiseable]
public string SomeValue
{
...
}
Just an idea but may not be a perfect solution.
Perhaps my problem is more architectural than functional, but I am trying to bind a TextBox to a command, and in that command, i'd like to pass multiple parameters (i.e. a custom object). Not sure how to do this in declarative fashion (xaml).
ViewA (sits on top of ViewModelA)
it has a TextBox, which is bound to CommandX
ViewB (sits on top of ViewModelB) (this is in fact another user control within the same Window as ViewA)
When commandX fires, ViewModelB needs to execute some method. ViewModelB needs to be updated with multiple properties before it can execute that method.
My question is, how can i structure my command/architecture, so that ViewModelB has sufficient information to execute its method.
side note: ViewModelA has all the necessary info for ViewModelB to execute its method. but, i don't want to get it from there, because later i'd want CommandX to be executed from different views
update
it appears that i could set CommandParameter property separately, which could likely be bound to a complex type in ViewModelA. That should be sufficient to suff all necessary properties into it.
awesome
this worked! here is what my parmeter property looks like on ViewA
public ExecuteQueryCommandParameters ExecuteQueryParameters {
get {
var p = new ExecuteQueryCommandParameters();
p.AllColumns = ColumnsMaster;
p.DatabaseName = SelectedDatabase;
p.ServerName = SelectedServer;
p.TopRows = 20;
p.ViewModelName = "MainDataView";
return p;
}
}
and the button implementation
<Button Command="{Binding ExecuteQuery}" CommandParameter="{Binding ExecuteQueryParameters}">Top 20</Button>
update
there is a small road block with this solution. the commandparameter is bound to the property. but it retrieves it at load time, not at the time of command being executed. I need it to happen at command execution time, so that all the properties have latest values. any ideas on how to achieve that ?
Here's how I would do it:
For this explanation, I'll assume all of the necessary information is in one class, called "MyCustomCommandParameters".
I'll also assume that your XAML sets the main parent control's data binding to ViewModelA.
Have ViewModelA expose a property of type MyCustomCommandParameters.
Then, on ViewB, create a DependencyProperty of a type MyCustomCommandParameters.
The handlers for this property would pass this information to ViewModelB.
In the XAML, bind the ViewB's "MyCustomCommandParameters" value to ViewModelA's "MyCustomCommandParameters" property.
It's a bit of one-off plumbing work, but then you would meet your goal of having the information passed from ViewModelA to ViewModelB by XAML binding, and the two models would not know about one another.
I've been doing the best I can to try to stay true to the separation recommended by the MVVM pattern. One thing I haven't figure out how to do correctly has to do with initializing my UserControls.
My most recent example of this has to do with a library that I wrote to talk to some low-level hardware. That assembly happens to have a UserControl that I can simply drop into any GUI that uses this hardware. All that is necessary for it to work is to set a reference to the object that has access to the low level methods.
However, that's where my problem lies -- currently, the UserControl is added to the GUI via XAML, where I define the namespace and then add the UserControl to my window. Of course, I have no control over its creation at this point, so the default constructor gets called. The only way to set the necessary reference for hardware control involves calling a method in the UC to do so. The ViewModel could feasibly call a method in the Model, e.g. GetController(), and then call the method in the UserControl to set the reference accordingly. The GUI can pass a reference to the UserControl to the ViewModel when said GUI creates the ViewModel, but this violates MVVM because the ViewModel shouldn't know anything about this control.
Another way I could deal with this is to not create the UserControl in XAML, but instead do it all from code-behind. After the ViewModel gets initialized and retrieves an initialized UserControl (i.e. one that has the low-level object reference set), it can set the Content of my Window to the UserControl. However, this also violates MVVM -- is there a way to databind the Content of a Window, TabControl, or any other element to a UserControl?
I'd like to hear if anyone has had to deal with this before, and if they approached it the first or second way I have outlined here, or if they took a completely different approach. If what I have asked here is unclear, please let me know and I'll do my best to update it with more information, diagrams, etc.
UPDATE
Thanks for the responses, guys, but I must not have explained the problem very well. I already use RelayCommands within the UserControl's ViewModel to handle all of the calls to the hardware layer (Model) when the user clicks in the control in the UserControl itself. My problem is related to initially passing a reference to the UserControl so it can talk to the hardware layer.
If I create the UserControl directly in XAML, then I can't pass it this reference via a constructor because I can only use the default constructor. The solution I have in place right now does not look MVVM-compliant -- I had to name the UserControl in XAML, and then in the code-behind (i.e. for the View), I have to call a method that I had added to be able to set this reference. For example, I have a GUI UserControl that contains the diagnostics UserControl for my hardware:
partial class GUI : UserControl
{
private MainViewModel ViewModel { get; set; }
public GUI( Model.MainModel model)
{
InitializeComponent();
ViewModel = new MainViewModel( model, this.Dispatcher);
ViewModel.Initialize();
this.DataContext = ViewModel;
diagnostics_toolbar.SetViewModel( ViewModel);
user_control_in_xaml.SetHardwareConnection( model.Connection);
}
}
where the outer class is the main GUI UserControl, and user_control_in_xaml is the UserControl I had to name in the GUI's XAML.
Looking at this again, I realize that it's probably okay to go with the naming approach because it's all used within the View itself. I'm not sure about passing the model information to user_control_in_xaml, because this means that a designer would have to know to call this method if he is to redo the GUI -- I thought the idea was to hide model details from the View layer, but I'm not sure how else to do this.
You will also notice that the main GUI is passed the Model in the constructor, which I assume is equally bad. Perhaps I need to revisit the design to see if it's possible to have the ViewModel create the Model, which is what I usually do, but in this case I can't remember why I had to create it outside of the GUI.
Am new to MVVM myself but here's a possible solution:
Create a property in your VM that is of the object type (that controls the hardware) and bind it to an attached property on your UserControl. Then you could set the property in your VM using dependency injection, so it would be set when the VM is created. The way I see it, the class that talks to the hardware (hardware controller) is a service. The service can be injected to your view model and bound to your UserControl. Am not sure if this is the best way to do it and if it is strict enough to all the MVVM principles but it seems like a possible solution.
if your question is: How do i show my viewmodel in the view? then my solution is always using viewmodelfirst approach and datatemplates.
so all you have to do is wire up your viewmodel via binding to a contentcontrol.content in xaml. wpf + datatemplates will do the work and instantiate your usercontrol for your viewmodel.
You are right, the ViewModel shouldn't know about anything in the View - or even that there is such a thing as a View, hence why MVVM rocks for unit testing too as the VM couldn't care less if it is exposing itself to a View or a test framework.
As far as I can see you might have to refactor things a little if you can. To stick to the MVVM pattern you could expose an ICommand, the ICommand calls an internal VM method that goes and gets the data (or whatever) from the Model, this method then updates an ObservableCollection property of the data objects for the View to bind to. So for example, in your VM you could have
private ICommand _getDataCommand;
public ICommand GetDataCommand
{
get
{
if (this._getDataCommand == null)
{
this._getDataCommand = new RelayCommand(param => this.GetMyData(), param => true);
}
return this._getDataCommand;
}
}
private void GetMyData{
//go and get data from Model and add to the MyControls collection
}
private ObservableCollection<MyUserControls> _uc;
public ObservableCollection<MyUserControls> MyControls
{
get
{
if (this._uc == null)
{
this._uc = new ObservableCollection<MyUserControls>();
}
return this._uc;
}
}
For the RelayCommand check out Josh Smiths MSDN article.
In the View you could either call the ICommand in the static constructor of your UC - I am guessing youwould need to add an event in your class for this - or call the ICommand from some sort of click event on your UC - maybe just have a 'load' button on the WPF window. And set the databinding of your UC to be the exposed observable collection of the VM.
If you can't change your UC at all then you could derive a new class from it and override certain behaviour.
Hope that helps a bit at least, like I say, have a look at Josh Smiths MVVM article as he covers the binding and ICommand stuff in there brilliantly.
If you set the DataContext of the Window or UserControl containing thisUserControl to the main view model, the user control can call SetHardwareConnection() on itself in its Loaded event (or DataContextChanged event handler).
If that's not possible because you're saying the UserControl is 'fixed', you should derive from it or wrap it up in another UserControl, which would serve as a MVVM 'adapter'.
(In order to bind the window: you could make the MainViewModel a singleton with a static Instance property and use DataContext="{x:Static MyClass.Instance}". A nice way to get things going quickly)
Note; this is based on my understanding that MVVM works because of Bindings.. I always bind the control to a ViewModel, not pass a ViewModel as a parameter.
Hope that helps!