My view has a control inside of it that is capable of generating an image that is saved at a path I can specify (along with some other data). I don't own this control and can't get the interface to generate an image changed. I'm not quite sure how to handle this with MVVM.
The quick and dirty way would be for my view to define a method that takes the desired path, and have the viewmodel call that method.
View:
public void GenerateImage(string path) {
_control.SaveImage(path);
}
ViewModel:
(actually this is the body of a Command) {
var path = GeneratePath();
_view.GenerateImage(path);
...
}
I don't like this because I get the feeling that viewmodels are not meant to directly reference the view, instead they represent the view's state and communicate via property bindings. It works, and I'm doing this while waiting on answers. I'd like to find a way around it.
I could get cute and have the view pass a reference to the control to a Command (I'm in Xamarin Forms) via the Execute() parameter, and have the command cast and make the call. This seems like lipstick on a pig since it makes the viewmodel still aware of a particular class inside the view. But in writing this paragraph I think I came up with a solution I like.
I /could/ create:
interface IGenerateImage {
void GenerateImage(string path);
}
The obvious implementation would delegate the call to an encapsulated control. I feel like if the view passes an IGenerateImage then I'm not creating the viewmodel-to-view dependency that I'm trying to avoid, and I can test the logic without needing to instantiate expensive UI classes.
I like that answer, but I'm pretty sure there's an obvious solution I'm missing. Is there some other useful pattern for handling it? Or is it not a big deal if the viewmodel references the view?
You never want the View Model to know anything about the View.
It's a little unclear what you can and can't change in your post, so I'm assuming you can change the V/VM, but not _control.
The easiest way is to create an event in the View Model that the View can subscribe to.
Something like this:
View:
// Constructor
public View()
{
// However you're setting your VM, i.e. DI or new-ing up the VM
// Subscribe to the event
vm.ImageGeneratedEvent += this.OnImageGeneratedEvent;
}
private void OnImageGeneratedEvent(object sender, ImageGeneratedEventArgs args)
{
// Call your SaveImage in the event handler
_control.SaveImage(args.Path);
}
View Model:
public event EventHandler<ImageGeneratedEventArgs> ImageGeneratedEvent;
// Command body
{
var path = GeneratePath();
// Send event to the View
this.NotifyImageGeneratedEvent(path)
}
private void NotifyImageGeneratedEvent(string path)
{
ImageGeneratedEventArgs args = new ImageGeneratedEventArgs(path);
if (this.ImageGeneratedEvent!= null)
{
this.ImageGeneratedEvent(this, args);
}
}
ImageGeneratedEventArgs:
public class ImageGeneratedEventArgs : EventArgs
{
public string Path { get; set; }
public ImageGeneratedEventArgs(string path)
{
this.Path = path;
}
}
Related
In a view model's constructor I have a command declaration that calls a method:
OpenGroupCommand = new DelegateCommand(OnOpenGroupExecute);
And the method looks like:
private void OnOpenGroupExecute(object obj)
{
string groupName = (string)obj;
Application.Current.MainPage.Navigation.PushAsync(new GroupPage(groupName));
}
How can I test, that groupName is passed to another view model correctly? In another view model groupName parameter is sent to GroupName property on VM instance:
public class GroupPageViewModel : ViewModelBase, IGroupPageViewModel
{
private string _groupName;
public GroupPageViewModel(string groupName)
{
LoadGroupName(groupName);
}
public void LoadGroupName(string groupName)
{
GroupName = groupName;
}
public string GroupName
{
get
{
return _groupName;
}
set
{
_groupName = value;
OnPropertyChanged();
}
}
}
On debug all works fine, but how can I unit test it? Where can I read a bit about testing and mocking stuff like this, even with Moq framework?
I believe your question is actually about how to test navigation between pages.
In the implementation of method OnOpenGroupExecute, because you are using Xamarin forms stuff to implement the navigation, you have to refer Xamarin Forms assemblies in your test project which makes the unit test depend on Xamarin Forms.
As suggested in this document https://learn.microsoft.com/en-us/xamarin/xamarin-forms/enterprise-application-patterns/ , try to create an interface for navigation and navigate with viewmodel (more details on https://github.com/dotnet-architecture/eShopOnContainers)
And in your unit test project, implement a fake navigation service class like below and inject into the DI container:
public class FakeNavigationService : INavigationService //this interface is from MS eShopOnContainer project
{
private List<ViewModelBase> _viewModels = new List<ViewModel>();
public Task NavigateToAsync<TViewModel>() where TViewModel : ViewModelBase {
//create viewModel object from DI container
//var viewModel = ......
_viewModels.Add(viewModel);
}
public ViewModelBase CurrentPageViewModel {
get {
if (_viewModels.Count() < 1) {
return null;
}
return _viewModels[_viewModels.Count() - 1];
}
}
}
This is just a suggestion. If you have implemented most of features in your app, it takes time to change navigate-with-page to navigate-with-viewmodel.
Well, let's see what you have:
you have some code in a private method, unless you make that public you won't be able to test it directly, because you can't call it. I am not considering here any tricks that allow you to call private methods.
what does that method do? It is not clear at all, it receives an object, we don't know what's in it. You're converting it to string, but what if it is not a string? Can you convert that object to a string? who knows.
So we have a method, that we don't know what it does, we don't know what it receives as parameters, we can't call it directly, but we want to test it. This is not a good position to be in.
Step back a bit and ask yourself, what are you really trying to test?
You said : How can I test, that groupName is passed to another view model correctly?
what does "correctly" mean? You need to define what it means for that string to be correct. This will give a test scenario you can work with.
I expect to receive an object, which looks like A and I want to convert it to a string which looks like B. Forget about viewmodels for now, that's just unimportant noise.
You can change the method into a public one and you can test that for different types of input data, you're getting the right result. This is literally, working with an object and extract some stuff from it. When that method is correct, you can guarantee that the viewmodel will receive the right input and that is good enough from a unit testing point of view.
You can of course add more tests for various inputs, you can test for correct failure conditions etc.
I have problem passing object to secondary view in MVVM light WPF. I have main view Model. follow of operation. I am able to wire things up using MVVM light and Modren UI navigation Services. The issue is that i am not able to send object of Main Customer view model to secondary View Model. I want to set data-context of target View from source View Model. I have tried this but does not seem to be working. I prefer no code behind and i have spent a lot of time without any success.
public virtual void NavigateTo(string pageKey, object parameter)
{
lock (_pagesByKey)
{
if (!_pagesByKey.ContainsKey(pageKey))
{
throw new ArgumentException(string.Format("No such page: {0}. Did you forget to call NavigationService.Configure?", pageKey), "pageKey");
}
var frame = GetDescendantFromName(Application.Current.MainWindow, "ContentFrame") as ModernFrame;
// Set the frame source, which initiates navigation
if (frame != null)
{
frame.Source = _pagesByKey[pageKey];
//i Dont know if this should work or not
frame.DataContext = parameter;
}
Parameter = parameter;
_historic.Add(pageKey);
CurrentPageKey = pageKey;
}
}
any help will be greatly appreciated. I just need to how i can set the datacontext of target View without using code behind. Thanks
There's multiple possibilities but one that does not create dependencies between your viewmodels is to use pub/sub system in MVVMLight. Basically it goes like this:
When you select some entity from your view and transition to another, viewmodel sends a message that carriers that given entity along. In the other viewmodel you receive the message and set some property accordingly (for editing, adding new entity, etc.)
// mainviewmodel
Messenger.Default.Send(new MyMessage(myObj));
// otherviewmodel
Messenger.Default.Register<MyMessage>(this, message =>
{
/* do something with message.MyObj */
});
// mymessage
public class MyMessage : MessageBase
{
...
public MyObj MyObj { get; set; }
}
I have a Silverlight/WPF application I'm enhancing. I have a UserControl and it needs to disable or enable some controls when a property on the model changes. It has to do some other logic, so I can't just bind them to the property.
In the control's code behind, I have a reference to the model. I know there is a way to bind to certain properties, and I know how to do it in XAML, but not in the code-behind.
I've seen a lot of instances say to use the INotifyPropertyChanged interface, but it doesn't seem to apply in this case.
An example of what I'm trying to do:
public partial class MyControl : UserControl
{
private readonly MyModel _model;
public MyControl(MyModel model)
{
_model = model;
// bind to model's ImportantThing property here
}
...
// Some method gets called when property changes
...
}
public class MyModel
{
...
public bool ImportantThing
{
get { return _importantThing; }
set
{
_importantThing = value;
// This is existing code and notifies some controls, but not the ones
// I'm interested in. It should notify MyControl as well. I know in
// most applications, this is OnPropertyChanged();
RaisePropertyChanged("ImportantThing");
}
}
}
Any pointers?
Some Pointers....
Your issue\solution sounds like a task for a ValueConverter. But first, I can see code in the UserControl code-behind file, you really should adopt and apply the MVVM pattern... OK there is a [steep] learning curve and sometimes you wonder if it's worth the effort (know I did when I started with XAML)... But take my word for it.... MVVM, there simply in no other way to develop using WPF. If you try to apply the WinForms UI Logic to WPF it will become an unmaintainable, unmanageable monolithic pile of spaghetti code....
you might find this link to Rachel Lim's Blog useful....
https://rachel53461.wordpress.com/category/mvvm/
and for ValueConverter take a look at this.....
http://www.wpftutorial.net/ValueConverters.html
I apologize, my original question wasn't all that clear, but I've found a solution. It turns out the UserControl (MyControl in my original example) was already watching the Model for changes:
_myModel.PropertyChanged += Model_PropertyChanged;
In the existing callback (Model_PropertyChanged()), I just looked for the property I was interested in and added everything else I needed:
void Model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "StatusEnabled")
{
// do stuff
}
else if (e.PropertyName == "ImportantThing")
{
// my stuff
}
}
Thanks for everyone's input!
I've been looking in the MVP pattern for a while, and managed to create some simple MVP-compliant applications.
I am now trying to apply the pattern to a more complex application, and I have some doubts on the best way of doing that.
My application has a single WinForm, with two buttons for loading two different kinds of data. My view interface looks like the following:
interface IView_MainForm
{
// Load input
//
event EventHandler<InputLoadEventArgs> LoadInput_01;
event EventHandler<InputLoadEventArgs> LoadInput_02;
bool Input01_Loaded { get; set; }
bool Input02_Loaded { get; set; }
}
The IView is referenced in my presenter via constructor injection:
public Presenter_MainForm(IView_MainForm view)
{
this.View = view;
this.View.LoadInput_01 += new EventHandler<InputLoadEventArgs>(OnLoadInput_01);
this.View.LoadInput_02 += new EventHandler<InputLoadEventArgs>(OnLoadInput_02);
}
So far, so good. When the user clicks any of the two buttons for loading data, a LoadInput_## event is raised, the Presenter is handling it, checks the input for errors and structures it according to my data model.
My next step would be displaying the processed data back in the View.
I'm striving to keep my View as passive and "dumb" as possible, assuming it knows nothing of the Presenter (it doesn't subscribe to its events, the Presenter sends data to the View by calling IView methods instead), let alone of the Model.
How am I supposed to populate a control like a TreeView, if the View has no idea of what the data model looks like?
Also, am I getting the whole MVP thing right, or is there anything I have missed?
There is nothing wrong with having complex type properties in your View. Let's say you have some ComplexType.
class ComplexType
{
public string ParentNode {get;set;}
public List<string> ChildNodes {get;set;}
// some other properties
}
Let's also assume ComplexType is data model for your TreeView. It is perfectly fine with MVP pattern to have properties on your View that will have ComplexType. So having something like this is perfectly fine
interface IView_MainForm
{
// Load input
//
event EventHandler<InputLoadEventArgs> LoadInput_01;
event EventHandler<InputLoadEventArgs> LoadInput_02;
bool Input01_Loaded { get; set; }
bool Input02_Loaded { get; set; }
ComplexType Input01Data {get;set;} // you might actually have some code in get/set setters
ComplexType Input02Data {get;set;} // you might actually have some code in get/set setters
public void SetInput01Data(ComplexType input01Data)
{
Input01Data = input01Data;
// some other stuff
}
}
And since your Model is for View that has 2 inputs, your Model could look something like this
public interface IModel
{
public ComplexType Input01Data {get;set;}
public ComplexType Input02Data {get;set;}
}
Now in your Presenter you would just handle event fired from View, populate Model and set properties on View
class Presenter
{
private IModel _myModel...
private IRepository _repository;
public Presenter(IView_MainForm view, IRepository repository)
{
_repository = repository;
this.View = view;
this.View.LoadInput_01 += new EventHandler<InputLoadEventArgs>(OnLoadInput_01);
this.View.LoadInput_02 += new EventHandler<InputLoadEventArgs>(OnLoadInput_02);
}
public void OnLoadInput_01(object sender, InputLoadEventArgs e)
{
// get data based on passed arguments (e.SomeProperty)
// construct IModel
myModel = _repository.GetData(e.SomeProperty);
// pass data to IView_MainForm
View.SetInput01Data(myModel.Input01Data);
}
}
And regarding your concern
I'm striving to keep my View as passive and "dumb" as possible,
assuming it knows nothing of the Presenter (it doesn't subscribe to
its events, the Presenter sends data to the View by calling IView
methods instead), let alone of the Model.
Your View still doesn't know anything about Presenter nor Model. It just fires events, get data from Presenter and binds its controls. And you have testability in place (please note this Unit Test is pseudo code, since I don't know how you retrieve data, what input you required in button click event etc...) .
[Test]
public void ShouldLoadInput01DataOnButtonClick()
{
// Arrange
IModel data = // create dummy data
Mock<IView_MainForm> clientsViewMock = new Mock<IView_MainForm>();
Mock<IRepository> clientsRepositoryMock = new Mock<IRepository>();
clientsRepositoryMock.Setup(repository => repository.GetData(something)).Returns(data.Input01Data);
var presenter = new Presenter(clientsViewMock.Object, clientsRepositoryMock .Object);
// Act
clientsViewMock.Raise(view => view.LoadInput01 += null, new InputLoadEventArgs());
// Assert
clientsViewMock.Verify(view => view.SetInput01Data(data.Input01Data), "Input01 data expected be set on button click.");
}
I am working on a Windows Phone 7 application. Now I need to switch the view after a user tapped the designated button which takes user to another view.
Which component, theoretically, in MVVM should be in charge of the navigation, i.e. switching views? Code snippets would be good to show demonstration.
I have tried inserting the switching code in View and it works alright, but I encountered a situation where I call an asynchronous web service and would like to navigate user to the new view only after the operation is done, the navigation code should be inside the event handler.
Thank you.
P/S: My project's deadline is coming soon, I have no time to rebuild my project using MVVM tools, such as MVVM Light, Caliburn Micro, and etc.
I put a Navigate methods in the base class that all my ViewModel's share:
protected void Navigate(string address)
{
if (string.IsNullOrEmpty(address))
return;
Uri uri = new Uri(address, UriKind.Relative);
Debug.Assert(App.Current.RootVisual is PhoneApplicationFrame);
BeginInvoke(() =>
((PhoneApplicationFrame)App.Current.RootVisual).Navigate(uri));
}
protected void Navigate(string page, AppViewModel vm)
{
// this little bit adds the viewmodel to a static dictionary
// and then a reference to the key to the new page so that pages can
// be bound to arbitrary viewmodels based on runtime logic
string key = vm.GetHashCode().ToString();
ViewModelLocator.ViewModels[key] = vm;
Navigate(string.Format("{0}?vm={1}", page, key));
}
protected void GoBack()
{
var frame = (PhoneApplicationFrame)App.Current.RootVisual;
if (frame.CanGoBack)
frame.GoBack();
}
So the ViewModel base class executes the navigation if that's what you are asking. And then typically some derived ViewModel class controls the target of the navigation in response to the execution of an ICommand bound to a button or hyperlink in the View.
protected SelectableItemViewModel(T item)
{
Item = item;
SelectItemCommand = new RelayCommand(SelectItem);
}
public T Item { get; private set; }
public RelayCommand SelectItemCommand { get; private set; }
protected override void SelectItem()
{
base.SelectItem();
Navigate(Item.DetailPageName, Item);
}
So the View only knows when a navigate action is needed and the ViewModels know where to go (based on ViewModel and Model state) and how to get there.
The view should have a limited number of possible destinations. If you have to have a top-level navigation on every page, that should be part of your layout or you can put them in a child view.
I put navigation outside of MVVM in a class that is responsible for showing/hiding views.
The ViewModels use a messagebroker with weakevents to publish messages to this class.
This setup gives me most freedom and doesn't put any responsibilities in the MVVM classes that do not belong there.