FubuMvc: If I move my controller and views into a folder I get a 404 - fubumvc

I'm new to FubuMvc and I'm just playing around with it on a small project.
I have the default nuget package fubu configuration, and I'm using web forms view engine:
public ConfigureFubuMVC()
{
// This line turns on the basic diagnostics and request tracing
IncludeDiagnostics(true);
// All public methods from concrete classes ending in "Controller"
// in this assembly are assumed to be action methods
Actions.IncludeClassesSuffixedWithController();
// Policies
Routes
.IgnoreControllerNamesEntirely().IgnoreControllerFolderName()
.IgnoreMethodSuffix("Html")
.RootAtAssemblyNamespace();
// Match views to action methods by matching
// on model type, view name, and namespace
Views.TryToAttachWithDefaultConventions();
// View Engine
this.Import<WebFormsEngine>();
}
I've created a controller and a view in my site root, like so:
~/IndexController.cs
namespace MovieApp
{
public class IndexController
{
private MoviesDBEntities _db = new MoviesDBEntities();
public MovieIndexViewModel Index()
{
return new MovieIndexViewModel { Movies = _db.Movies.ToList() };
}
public class MovieIndexViewModel
{
public IEnumerable<Movie> Movies { get; set; }
}
}
}
and the View that goes with it:
~/Index.aspx
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" MasterPageFile="/Site.Master" Inherits="MovieApp.Index" %>
...
When I browse to ~/Index it works fine.
Now I want to move my controller into a new folder, "Movies". So I move the controller and the view, and I change the namespace on the controller to be MoviesApp.Movies.
When I navigate to ~/Movies/Index, it hits a breakpoint in my IndexController.Index() ActionMethod, but then a 404 is displayed.
Any ideas?

I assume that you're using the WebForms engine, right? For WebForms view resolution, FubuMVC makes the assumption that the namespace of the view exactly matches the view location. When you moved the views around those two things no longer match. If you've got R# installed, just open the code behind and have it adjust the Namespace -- and make sure the namespace gets changed on the aspx as well.
The better advice is probably to switch to the Spark view engine or Razor support in FubuMVC is already in flight.

Related

Reactive UI / Blazor: Why is a View that inherits from ReactiveInjectableComponentBase<T> rendered twice?

When I inherit a View from ReactiveInjectableComponentBase<T> the body of the View will be "rendered" twice. I noticed this because a #foreach loop was executed twice on activation of the view. This effect happens no matter how simple the View and the ViewModel are.
Simple reproduction repo: https://github.com/Lukzy/ReactiveUI.ExecutedTwiceBug
Bug filed on ReactiveUI GitHub: https://github.com/reactiveui/ReactiveUI/issues/3256
I used the following code to simulate the behavior.
ViewWithoutInherits: "View rendered" will be executed once.
#page "/whatever"
#using System.Diagnostics
#{
Trace.TraceInformation("View renderer");
}
ViewWithInherits: "View rendered" will be executed twice.
#page "/whatever"
#using System.Diagnostics
#inherits ReactiveInjectableComponentBase<ViewModel>
#{
Trace.TraceInformation("View renderer");
}
ViewModel
using ReactiveUI;
public class TestViewModel : ReactiveObject
{
}
Am I missing something or is this a bug? Any help or hint is highly appreciated.
UPDATE #1:
I replaced the trace code with the suggestion from Dylan Barquilla.
#code {
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
Trace.TraceInformation("View renderer in FIRST RENDER");
}
Trace.TraceInformation("View renderer");
base.OnAfterRender(firstRender);
}
}
Output from ViewWithoutInherits:
View renderer in FIRST RENDER
View renderer
Output from ViewWithInherits:
View renderer in FIRST RENDER
View renderer
View renderer
UPDATE #2:
ViewWithInherits: foreach-loop will be executed twice.
#page "/whatever"
#inherits ReactiveInjectableComponentBase<ViewModel>
#foreach (var value in ViewModel.Values)
{
<p>#(value)</p>
}
ViewWithInject: foreach-loop will be executed once.
#page "/whatever"
#foreach (var value in ViewModel.Values)
{
<p>#(value)</p>
}
#code {
[Inject]
public TestViewModel ViewModel { get; set; }
}
ViewModel
using ReactiveUI;
public class TestViewModel : ReactiveObject
{
public List<string> Values{ get; set; } = new()
{
"a",
"b"
};
}
EDIT 2: after cloning the repo, to prove what I want to say.
I just added a button on both components, with a simple handler (with no real effect).
<button #onclick="#OnClickHandler">Click on me</button>
//-- CODE PART --
public string Test { get; set; } = string.Empty;
private void OnClickHandler()
{
Test = "New";
}
You will see that every time you click on the button, the Foreach loop was executed [N] times will be updated with a two increments.
It is a perfect normal behavior in Blazor. A component being re render is a normal thing, and happen A LOT of time, basically every UI interaction can force the component to re render. So it should not cause you trouble, and Blazor has some useful methods (SetParametersAsync or OnInitialized) that can be used to apply logic only once, not every time the component is rendering.
If a component is inherit of another component, it could be the parent to cause this re rendering, by apply some logic (adding fields, updating fields etc.).
EDIT: I just checked the new outputs that you get, and I think the explanation is that the library that you use update or inject something in the component, after its first rendering, for example adding some fields on it or anything. It is a classic behavior with some other Blazor libraries, so I guess it is pretty normal - but you can ask in the Github / forum of the library you use to be sure.
So it basically means that if you want your code do be executed only once, I suggest you to use the following:
#code {
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
// Do whatever you need here, it will be executed only once
}
base.OnAfterRender(firstRender); // this line is important for Blazor lifecycle
}
}

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.

How to bind view model to a UserControl in MVVMCROSS?

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.

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

MvvmCross: How to navigate to something besides a ViewModel?

What would I put in my MvxCommand to navigate to a simple URL? All mobile platforms have a mechanism to ask the OS for an Activity or ViewController that can display the contents of a URL. How would I do that with MvvmCross? One way that I know of is to put special stuff in the presentationBundle and/or parameterBundle when calling ShowViewModel that the presenter can detect to do the special OpenUrl command. But is that the best way??
There is a plugin which enables this - https://github.com/slodge/MvvmCross/tree/v3/Plugins/Cirrious/WebBrowser
If that plugins is loaded, then a viewmodel can use:
public class MyViewModel : MvxViewModel
{
private readonly IMvxWebBrowserTask _webBrowser;
public MyViewModel(IMvxWebBrowserTask webBrowser)
{
_webBrowser = webBrowse;
}
public ICommand ShowWebPage
{
get { return new MvxCommand(() => _webBrowser.ShowWebPage("https://github.com/slodge/mvvmcross");
}
}
You can see this used in, for example:
https://github.com/slodge/MvvmCross-Tutorials/blob/master/Sample%20-%20CirriousConference/Cirrious.Conference.Core/ViewModels/BaseViewModel.cs
https://github.com/slodge/MvvmCross-Tutorials/blob/master/Sample%20-%20CustomerManagement/CustomerManagement/CustomerManagement/ViewModels/DetailsCustomerViewModel.cs
If you ever need to create your own plugins, see https://speakerdeck.com/cirrious/plugins-in-mvvmcross