How to bind view model to a UserControl in MVVMCROSS? - mvvm

I am using the excellent Mvvmcross and Ninja Coder for Mvvmcross for building a cross platform app. For my windows store app I have created a view and a view model using Ninja coder. I have also created a UserControl which will be referenced in the view. Hence I need to bind the same viewmodel to the User control also. I have been trying to set the Data context of the user control to the singleton instance of viewmodel. I have set the data context of the user control like below.
public sealed partial class SearchResultsGridViewControl : UserControl
{
private SearchresultsViewModel _viewModel;
public SearchResultsGridViewControl()
{
this.InitializeComponent();
_viewModel = Mvx.IocConstruct<SearchresultsViewModel>();
this.DataContext = _viewModel;
}
}
But when I refer this User Control in my main view, it throws an error in XAML saying "Object Reference not set to an instance of an object. Cannot create an instance of SearchResultsGridViewControl".
This is my viewmodel:
public class SearchresultsViewModel : BaseViewModel
{
private ISearchResultsService _searchResultsService;
public SearchresultsViewModel(ISearchResultsService searchResultsService)
{
_searchResultsService = searchResultsService;
var items = _searchResultsService.DisplaySearchResults();
SchoolDetails = new ObservableCollection<School>(items);
}
private ObservableCollection<School> _schoolDetails;
public ObservableCollection<School> SchoolDetails
{
get { return _schoolDetails; }
set
{
_schoolDetails = value;
RaisePropertyChanged(() => SchoolDetails);
}
}
public ICommand RefineCommand
{
get
{
refineCommand = refineCommand ?? new MvxCommand(FilterSearchResultsBasedOnRefine);
return refineCommand;
}
}
public void FilterSearchResultsBasedOnRefine()
{
SchoolDetails = new ObservableCollection<School>(_searchResultsService.FilterSchoolsBasedOnRefine(MidDayMeals, PlayGround, DigitalClassroom, DayBoarding, TransportationFacility));
}
}
The grid view in my usercontrol is getting populated when it loads for the first time. But when RefineCommand is called to update the collection from the main view, the grid view in usercontrol is not getting updated. And I am guessing its because of that error earlier in setting the data context of the user control to view model. Please let me know what could be going wrong. I have been banging my head about it for days.

I've been using MVVMCross with Windows Store fairly recently. Without looking back at my code, I'm pretty sure that the Datacontext will inherit from it's parent unless overridden.
So as long as the MvxPage that you have presented has a viewmodel, any user control that you add to it, either in XAML or in code-behind, should share the same data context. If you are looking at doing some MVVMCross data-binding from the User Control, you should probably make sure your User Control implements IMvxStoreView, and ensure that the ViewModel property is set to the value of DataContext.
Hope that help.
Cheers,
Tristan

I think your first problem "Object Reference not set to an instance of an object" is a design time only issue - because you are trying to set the viewmodel using Mvx. at design time. You can workaround this issue if you want to by using design time viewmodels and possibly also by using one of the design time helpers (see https://github.com/MvvmCross/MvvmCross/blob/v3.1/CrossCore/Cirrious.CrossCore.Wpf/Platform/MvxDesignTimeHelper.cs).
I've no idea what your second problem is "The grid view in my usercontrol is getting populated when it loads for the first time. But when RefineCommand is called to update the collection from the main view, the grid view in usercontrol is not getting updated" - this sounds like an issue either in your xaml or in the results returned from FilterSearchResultsBasedOnRefine. From the current level of detail, I can't see what it is. My "gut feeling" is that the problem won't be MvvmCross specific - it'll just be a general Mvvm/data-binding issue.

Related

JavaFX Model View ViewModel where should I implement tasks?

There are several tutorials and examples on this topic out there but they are all a sort of generic build only in one class to show how it works generally.
So my question is when I would like to follow the MVVM pattern where I have to implement all my tasks?
Given the following:
Model:
class Model {
/* When I place the Task here how can I deal with arguments and results from ViewController? */
public BufferedImage bigTask (String this, String and, Image that){
// Some code to build a BufferedImage
}
}
ViewModel:
class ViewController {
private BufferedImage myBufferedImage;
#FXML
private Button aButton;
/*Should I implement my Task here? But how I get information about progress? */
final Task<Integer> myTask = new Task<Integer>(){
#Override
protected Integer call() throws Exception{
updateProgress( // How to get here? Is it the right place? )
return null;
}
};
#FXML
void setOnAction(ActionEvent actionEvent){
myBufferedImage = Model.bigTask("this", "that", new Image("path"));
}
}
Hope I could explain the problem.
Thanks in advance!
In general your tasks should be implemented in the ViewModel.
The actual implementation of business logic should be done in the Model for example in a service class. The ViewModel can then use this service and handle all the ui specific actions like creating a Task for async execution and updating a progress value. However the ViewModel may not directly update a ProgressIndicator but instead the viewModel could have a DoubleProperty "progress" that is updated in the ViewModel. In the ViewController/CodeBehind you bind the actual ProgressIndicator to this progress property of the ViewModel. This way the ViewModel is independent from actual UI controls and the View doesn't contain any business logic.
Your example is a little bit special I think. Normally I would say that "BufferedImage" is a ui specific class that only belongs to the View and not the ViewModel nor the Model. However your example looks like BufferedImage is the result of a business action. In this case I would create a ObjectProperty<BufferedImage> in your ViewModel and put the task to load the image in the ViewModel too. In your ViewController I would add a listener to this property and put the image into the ui when it changes.
This way your View class is independet of how the image is loaded.

Best practice MVVM navigation using Master Detail page?

I want to follow the MVVM pattern as much as possible, but I don't know if I am doing the navigation quite well. Note that I am using a MasterDetail page and I want to maintain the Master page, changing only the Detail side when I navigate.
Here is the way I navigate from my ViewModel. In this example, from ViewModelOne to ViewModelTwo:
public class ViewModelOne : ViewModelBase
{
private void GoToViewTwo()
{
var viewTwo = new ViewTwo(new ViewModelTwo());
((MasterView)Application.Current.MainPage).NavigateToPage(viewTwo);
}
}
MasterView implementation:
public class MasterView : MasterDetailPage
{
public void NavigateToPage(Page page)
{
Detail = new NavigationPage(page);
IsPresented = false;
}
}
ViewTwo implementation:
public partial class ViewTwo : PageBase
{
public MenuView(ViewModelTwo vm)
: base(vm)
{
InitializeComponent();
}
}
PageBase implementation:
public class PageBase : ContentPage
{
public PageBase(ViewModelBase vmb)
{
this.BindingContext = vmb;
}
}
Is this the best approach (and best performance) for do the navigation? When I do some navigations, the app starts to run slower and maybe there is something I am not doing fine.
Is this the best approach for do the navigation showing always a MasterDetail page?
Thanks.
I think you're certainly on the right track, however there are a few issues here:
Firstly, you should not be instantiating Views in your view model. As soon as your View Model becomes aware of the view, then you've pretty much broken the pattern.
var viewTwo = new ViewTwo(new ViewModelTwo());
Your view creation should be the responsibility of the master view. In fact, you don't even need to worry about creating views, as you can use a DataTemplate for that. I'll explain that later.
Firstly, we need to separate your View Models from the Views, here is what I propose:
You'll need some kind of base class or interface for your view models in order to keep things generic, you'll see why in a moment. Let's start out with a simple example:
public abstract class ViewModel : INotifyPropertyChanged
{
public event EventHandler OnClosed;
public event EventHandler OnOpened;
//Don't forget to implement INotifyPropertyChanged.
public bool IsDisplayed { get; private set; }
public void Open()
{
IsDisplayed = true;
//TODO: Raise the OnOpened event (Might be a better idea to put it in the IsDisplayed getter.
}
public void Close()
{
IsDisplayed = false;
//TODO: Raise the OnClosed event.
}
}
This of course is a very simple base view model, you can extend on this later, the main reason for this is to allow you to create a master view model which will be responsible for displaying your current page. Here's a simple example of a master view model:
public class MasterViewModel : INotifyPropertyChanged
{
//Don't forget to implement INotifyPropertyChanged.
public ViewModel CurrentPage { get; private set; }
public MasterViewModel()
{
//This is just an example of how to set the current page.
//You might want to use a command instead.
CurrentPage = new MovieViewModel();
}
//TODO: Some other master view model functionality, like exiting the application.
}
Please note that INotifyPropertyChanged would probably be better in some kind of base class, instead of having to re-implement the same code over and over.
Now the MasterViewModel is pretty simple, it just holds the current page, however the purpose of having the master is to allow for application level code to be executed, like closing the app, that way you're keeping this logic away from your other view models.
Right, now onto the good stuff.
Your detail has a relationship to it's parent, therefore it would make sense to say that it is the responsibility of the parent to manage it. In this case, your master-detail view model would look something like this:
public class MovieViewModel : ViewModel
{
protected PickGenreViewModel ChildViewModel { get; private set; }
public MovieViewModel()
{
ChildViewModel = new PickGenreViewModel();
//TODO: Perhaps subscribe to the closed event?
}
//Just an example but an important thing to note is that
//this method is protected because it's the MovieViewModel's
//responsibility to manage it's child view model.
protected void PickAGenre()
{
ChildViewModel.Open();
}
//TODO: Other view model functionality.
}
So, now we've got some kind of view model structure here, I bet you're asking "What about the views?", well, that's where the DataTemplate comes in.
In WPF, it's possible to assign a view to a Type, for example, you can assign the MovieView to the MovieViewModel in XAML, like this:
xmlns:Views="clr-namespace:YourNamespace.Views"
xmlns:ViewModels="clr-namespace:YourNamespace.ViewModels"
...
<DataTemplate DataType="{x:Type ViewModels:MovieViewModel}">
<Views:MovieView/>
</DataTemplate>
Ok great!, now to get the Master View to actually display the current page's view, you simply need to create a ContentPresenter, and bind it's Content to the CurrentPage. Your Master View will look something like this:
<Window
...
xmlns:ViewModels="clr-namespace:YourNamespace.ViewModels">
<Window.DataContext>
<ViewModels:MasterViewModel/>
</Window.DataContext>
<Grid>
<ContentPresenter Content="{Binding CurrentPage}"/>
</Grid>
To extend this further, it's not only the MasterView that needs to contain a ContentPresenter for it's child, it is also the MovieView which needs one for it's child PickGenreViewModel. You can use the same method again:
<Grid>
<!-- The main view code for the movie view -->
...
<Border Visibility="{Binding ChildViewModel.IsDisplayed, Converter=...">
<ContentPresenter Content="{Binding ChildViewModel}"/>
</Border>
</Grid>
Note: Use a boolean to Visibility converter to determine whether to display the child content.
Using this method you don't have to worry about instantiating any views, as the DataTemplate and ContentPresenter handles that for you, all you need to worry about is mapping the view models to the appropriate view.
Phew! That was a lot to take in.
The main points to take away from this are:
You shouldn't be creating views in your view models, remember, UI is UI, Data is Data.
View model responsibilities lie with whoever owns them, for a parent-child relationship, it makes sense to let the parent manage the child, as opposed to a view model manager.
A final note is that there are certainly more than one other ways of achieving this, as I just mentioned, some kind of view and view model manager to be responsible for creating/removing views and view models.

Where is good place to register Messenger responsible for showing Windows to ensure MVVM pattern Separation of Concerns and Testability not violated?

Scenario:
MainWindow has a Menu About which relates to AboutWindow.
About Meny is triggered by command:
<MenuItem Header="_About" Command="{Binding OpenAbout}"/>
OpenAbout is property like that:
private RelayCommand _openAbout;
public RelayCommand OpenAbout
{
get
{
return _openAbout ?? (_openAbout = new RelayCommand(() => Messenger.Default.Send(new NotificationMessage("ShowAboutView"))));
}
}
Notification message is registered in App.cs class as follows:
static App()
{
DispatcherHelper.Initialize();
}
public App()
{
RegisterMessenger();
}
public void RegisterMessenger()
{
Messenger.Default.Register<NotificationMessage>(this, ProcessShowAboutView);
}
private void ProcessShowAboutView(NotificationMessage message)
{
AboutWindow view = new AboutWindow();
view.Show();
}
I analysed another questions like that:
How to open a new window using MVVM Light Toolkit
WPF MVVM - How to Show a view from MainWindowViewModel upon Clicking on button
I like Messenger functionality but however I am not sure If above solution is a good one.
I would be thankful for any advise!
As depicted above, Registering messages is done in App Config.
I consider it not be a good place therefore I need to know what place would be better.
Another place to consider would be Locator
I personaly would register the messages in App.xaml.cs in the OnStartup method (WPF) and in the set up method of the unit test (dont forget to unregister everything in the tear down method).

Show AlertDialog from ViewModel using MvvmCross

I am using MvvmCross for creation my Android-app and I faced with the following problem:
When I'm trying to show AlertDialog, that was created in ViewModel, the
"Unhandled Exception: Android.Views.WindowManagerBadTokenException" appears.
public class MyViewModel : MvxViewModel
{
public ICommand ShowAlertCommand { get; private set; }
public AuthorizationViewModel()
{
ShowAlertCommand = new MvxCommand(() =>
{
var adb = new AlertDialog.Builder(Application.Context);
adb.SetTitle("Title here");
adb.SetMessage("Message here");
adb.SetIcon(Resource.Drawable.Icon);
adb.SetPositiveButton("OK", (sender, args) => { /* some logic */});
adb.SetNegativeButton("Cancel", (sender, args) => { /* close alertDialog */});
adb.Create().Show();
});
}
}
When I was researching I have found that it happens because of transmission of the reference to the Context but not on the Activity in the AlertDialog.Builder.
In this topic I found the following decision:
Receive references to the current Activity through the use of GetService(), but I didn't found mvvmcross plugins for work with IMvxServiceConsumer, IMvxAndroidCurrentTopActivity interfaces.
My question is can I show AlertDialog from ViewModel? And how can I get the reference to Activity, but not to the Application.Context?
And what is the correct way to close AlertDialog that the user would stay on the current View?
In general, you should try not to put this type of code into ViewModels
because ViewModels should stay platform independent
because ViewModels should be unit testable - and it's hard to unit test when the code shows a dialog
I'd also recommend you don't put code like this inside a ViewModel Constructor - these constructors are generally called during navigations and displaying a Dialog during a transition is likely to be problematic.
With those things said, if you do want to get hold of the current top Activity within any code, then you can do this using the IMvxAndroidCurrentTopActivity
public interface IMvxAndroidCurrentTopActivity
{
Activity Activity { get; }
}
Using this, any code can get the current Activity using:
var top = Mvx.Resolve<IMvxAndroidCurrentTopActivity>();
var act = top.Activity;
if (act == null)
{
// this can happen during transitions
// - you need to be sure that this won't happen for your code
throw new MvxException("Cannot get current top activity");
}
var dlg = new AlertDialog.Builder(act);
//...
dlg.Create().Show();
The use of IMvxAndroidCurrentTopActivity is discussed in MvvmCross: How to pass Android context down to MvxCommand?
The approach taken in that question/answer is also one of the ways I would generally approach showing dialogs from a ViewModel:
I would create an IFooDialog interface
Ideally I would probably make this interface asynchronous - e.g. using async or using an Action<DialogResult> callback parameter
on each platform I would implement that in the UI project
the ViewModels can then use IFooDialog when a dialog is needed and each platform can respond with an appropriate UI action
This 'Dialog Service' type of approach is common in Mvvm - e.g. see articles like http://www.codeproject.com/Articles/36745/Showing-Dialogs-When-Using-the-MVVM-Pattern (although that article is very Windows specific!)
There are also a few other questions on here about MvvmCross and dialogs - although they may contain reference to older v1 or vNext code - e.g. Alerts or Popups in MvvmCross and Unable run ProgressDialog - BadTokenException while showind

How to handle property sheet from customized editor in eclipse plugin development?

I have to bind my editor widget objects in property sheet.So that i can the property of my widget from property view.
Please help me on this, if possible provide me some code snippets.
You have a good example in the Getting started with Properties
Using the Properties view is simple enough.
Since it shows properties for the selected object, the first step to using it is to make sure that the workbench selection service knows about the object selected in your view. There’s an entire Eclipse Corner article written on the subject of the selection service
public void createPartControl(Composite parent) {
viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
viewer.setContentProvider(new ViewContentProvider());
viewer.setLabelProvider(new ViewLabelProvider());
getSite().setSelectionProvider(viewer);
viewer.setInput(getViewSite());
}
Once you have your view contributing to the workbench selection, you need to make sure that the objects that your view is selecting contribute properties
(extract)
public class Person implements IPropertySource {
private String name;
private Object street;
private Object city;
public Person(String name) {
this.name = name;
this.street = "";
this.city = "";
}
public Object getEditableValue() {
return this;
}
public IPropertyDescriptor[] getPropertyDescriptors() {
return new IPropertyDescriptor[] {
new TextPropertyDescriptor("name", "Name"),
new TextPropertyDescriptor("street", "Street"),
new TextPropertyDescriptor("city", "City")
};
}
I indicated earlier that this solution is “not necessarily [the] most correct”. This is because, for this to work, my domain object needs to know about the very view-centric (and Eclipse-centric) notion of being a property source; in short, there is a tight-coupling between the model and view and this not a good thing™.
Using adapter is a better approach, as described in this article:
Person should implement IAdaptable.
See also this recent article on how to create a custom property view
how to hack the Properties View to listen only to a specific view.
The isImportant() method is the one which decides whether to create an IPage for the specific IWorkbenchPart or not.
The idea is to override that method and return false for all the workbenchPart that we are not interested in. Lets create the view first:
<view
class="com.eclipse_tips.views.CustomPropertiesView"
icon="icons/sample.gif"
id="com.eclipse-tips.views.customePropertiesView"
name="My Properties View">
</view>
The CustomPropertiesView should extend PropertySheet and override the isImportant():
public class CustomPropertiesView extends PropertySheet {
#Override
protected boolean isImportant(IWorkbenchPart part) {
if (part.getSite().getId().equals(IPageLayout.ID_PROJECT_EXPLORER))
return true;
return false;
}
}
In this case, I'm making the view only to respond to Project Explorer and ignore other views
According to this thread, the same principle should be valid for an Editor instead of a View.
The property sheet listens to the workbench page selection provider.
The selection provider depends on what viewer/editor is active.
Each editor/viewer provides their own selection provider to use when that editor/viewer is active.
This way the property sheet doesn't care who is active, it just listens to the selection provider.
That way depending upon the view, a different set of properties are displayed.
For example, the Navigator view provides IResource selections, so the property sheet then displays IResource properties when the Navigator is active.
The Workbench Selection mechanism is illustrated in this article
The ISelectionListener is a simple interface with just one method.
A typical implementation looks like this:
private ISelectionListener mylistener = new ISelectionListener() {
public void selectionChanged(IWorkbenchPart sourcepart, ISelection selection) {
if (sourcepart != MyView.this && // 1
selection instanceof IStructuredSelection) { // 2
doSomething(((IStructuredSelection) selection).toList()); // 3
}
}
};
Depending on your requirements your listener implementation probably needs to deal with the following issues as shown in the code snippet above:
In case we also provide selections (e.g. a view or editor) we should exclude our own selection events from processing. This avoids unexpected results when the user selects elements within our part (1).
Check whether we can handle this kind of selection (2).
Get the selected content from the selection and process it (3).