How to access (custom) web helpers from within ViewStart? - c#

I have defined some custom helpers in my MVC3 Razor application (ASP.NET) and in the ViewStart code I would like to access my custom helpers.
I noticed that they are not accessible in _ViewStart which then seems understandable as ViewStart derives from ViewStartPage and not WebViewPage.
So I tried to define the helper in a custom ViewStart class but as it turns out I then need access to a ViewDataContainer to be able to initialize the helper.
So, the question is, how can I access my custom helper from ViewStart (or a custom ViewStartPage) and if not, can I then initialize the viewDataContainer constructor property with NULL. I don't expect needing any ViewData access in my custom ViewStartPage.
I also tried implementing the custom ViewStart class but it gives me this error:
CustomViewStart does not implement inherited abstract member 'System.Web.WebPages.WebPageExecutingBase.Execute()'
what should I do in that execute method? I don't want to do anything fancy in the customViewStart, just access my helper.

OK, it is after all possible as I suspected, just needed some nitty gritty technical details sorted out first.
public abstract class CustomViewStartPage : System.Web.Mvc.ViewStartPage {
public Helpers.InvariantHelper ConfigHelper { get; private set; }
public CustomViewStartPage() : base() {
ConfigHelper = new Helpers.InvariantHelper();
}
}
Now, I have defined several custom helpers in my WebViewPage custom base page and they do their work for views. However, in the ViewStart I needed to do certain stuff (here is only a trivial example) that did not require accessing the ViewContext (as I originally thought).
So, with this I can now have this in my _ViewStart.cshtml:
#* Views/_ViewStart.cshtml *#
#inherits MyNamespace.Web.Mvc.CustomViewStartPage
#{
var something = ConfigHelper.DisableParentLayout;
}
Sure, one can also use static members of a class as #MortenMertner indicated (a fresh view at the problem from outside) but in some cases that may not be what you truly want. This example here is senseless but serves as purpose to indicate how it could be accomplished for those who need.
If on the other hand a ViewContext needs to be accessed (another valid scenario) you may want to look at the post in my comment above which describes how to modify this custom view start page to access the context without errors.

From what I understand, the _ViewStart file is not a regular view. It can only be used to set defaults, such as the layout view, for other views.
For instance, this is the complete contents of my _ViewStart file:
#{ Layout = "~/Views/Shared/Layouts/Wide.cshtml"; }
If this holds true there would be no need for custom helpers in the file, and you're likely trying to solve a problem that you shouldn't have in the first place.
As an aside, you can add assemblies and namespaces in Web.config to avoid having to import them in specific views. I use this to import my models, enums and extension methods.

Related

Caliburn.Micro : locating Views when inheriting Viewmodels

I'm trying to connect multiple Viewmodels to a single View using caliburn micro. I found some solution but all seem quite heavy for something that seems to me a quite standard way of doing things.
The reason for being that is the following :
I'm implementing some views and their default ViewModels in an assembly. lets call the assembly HMI.Base with two classes
HMI.Base.ViewModels.BaseViewModel
HMI.Base.Views.BaseViewModel.
I'm then using this view from another assembly. lets call the assembly HMI.ConcreteApp
Usage is quite straightforward and overriding SelectAssemblies() does the trick so the view can be easily located.
Issues are rising when a ViewModel needs to get its data from a specific source. The solution that come to my mind is to extend my ViewModel and Add an element in its constructor being the source of my data and then do the necessary to link those data to the base viewmodel Properties.
So I create a class
HMI.Concrete.ViewModels.CustomViewModel
The implementation looks like this:
using HMI.Base.ViewModels;
public class CustomViewModel : BaseViewModel
{
public CustomViewModel (IConfiguration config, ILoggerFactory loggerFactory, IEventAggregator eventAggregator, DataSourceXYZ data) : base(config, loggerFactory, eventAggregator)
{
Logger.LogInformation("Constructing custom viewmodel that will display as HMI.Base.Views.BaseViewModel");
}
However the name has now changed, so the view cannot be located automatically. Is there a way to simply and clearly say "Use the base class view if no specific view exist with the same name" in the CustomViewModel ?
Without having to do some more fiddling in the bootstrapper, which seems bad to me (except if it isn't ) because all needed extra information are there (we know the base viewmodel why not use the base view to display the data)
I will provide already an answer for the best solution I found even if I would like better:
The solution is at this page
In short :
A custom attribute can be created to indicate the baseViewModel.
The Viewlocator can be changed in the Bootstraper to take it into consideration
And an extra step not in this article to avoid writing it in every application:
create A CustomBaseBoostrapper to write it only once and use it instead of BootstrapperBase.
It is not too bad, but I'm mainly wondering if there is not better and without changing the viewlocator.

How to access public methods from another Windows Phone page

Is there an shorter way to access public methods from other windows phone pages (e.g. from the MainPage.xaml) then this?
((MainPage)(((System.Windows.Controls.ContentControl)(App.RootFrame)).Content)).getMyPublicMethod()
If you are implementing it the way you are, you have a very bad separation of concerns. Normally, the view (aka the Page in this case) should not carry any functional components in terms of triggering actions that might be needed in another view.
A much better solution is to use the 'light' MVVM approach, where you have a core view model and then simply call actions from it, passing the necessary data from view to view. I've described a basic implementation here.
As far as I can see that's the only way. But you can create a wrapper to shorten syntax to access that page. For example, method wrapper :
public MainPage getMainPage()
{
return (MainPage)(((System.Windows.Controls.ContentControl)(App.RootFrame)).Content);
}
//usage :
getMainPage().getMyPublicMethod();
or property wrapper :
public MainPage OtherWindow
{
return (MainPage)(((System.Windows.Controls.ContentControl)(App.RootFrame)).Content);
}
//usage :
OtherWindow.getMyPublicMethod();

custom base page for razor helpers

I have a project where I have several custom properties and static methods available to the base WebPage class of a Razor view... ie, I have...
<system.web.webPages.razor>
<pages pageBaseType="myNamespace.WebView">
</pages>
</system.web.webPages.razor>
in my web.config. this is great. However, in my helper files, it is using the existing base class...
In this case, if I want to get access to some of those methods/properties/etc... I have to call the CurrentPage object, cast it to my custom baseclass object, then get the methods I want. In order to make the markup in the helpers simpler and cleaner, I would like to just define some static properties to grab the relevant data...
I have accomplished this nicely with some static methods and properties inside of a #functions{} block, but I would like to override the base class that exposes the CurrentPage property instead, so that I have it available by default in all of my helper files, and I won't have to repeat code...
It looks like the HelperPage implements the WebPageRenderingBase...
looking at the MSDN doc for the RazorPagesSection configuration, it doesn't look like there is anywhere else to modify it...
is there a similar configSection that I can define to override the HelperPage base type??? this would be super helpful. thanks.

MVC: Controller and View Communication (C#)

I am trying to build a program using model-view-controller.
Specifically:
I have a state manager that handles (initializing, loading, running, unloading etc... of) a set of MVCs, one at a time.
In each set, the single controller contains a reference to the relevant model and views.
Every model, view and controller are each derived from a parent class.
(i.e. abstract model, abstract view, abstract controller).
This allows me to keep much of the process in the parent classes, rather than adding duplicate code in each specific mvc set the state manager will handle.
Now the idea is for each View to call on its Controller methods, which in turn will call on its Model methods, and any resulting changes in the Model will be communicated back to the View through the Controller.
This is where I'm stuck. The only knowledge each view and model have of the controller is what is defined in its parent class. Which of course does not include the very specific methods the child-controller will need in order to properly handle the views and model.
Are there any ideas on how I can deal with this?
-Thanks.
You are probably closer to MVP than MVC. Anyway, getting around your problem may be possible using interfaces and generics.
Have your abstract classes take a generic interface:
public abstract class BaseView<TController>
{
protected TController Controller { get; private set; }
protected BaseView(TController controller)
{
Controller = controller;
}
}
Something to that effect.
Then your specific controller will simply implement the relevant interface.

ASP.NET MVC 3 Generic DisplayTemplates

I've just started a project using ASP.NET MVC 3. I'm building on top of an existing object system, so one of the first things I have to do is define display and editor templates for the various types that exist.
Is it possible in MVC to define a DisplayTemplate with a generic argument? For example, we have a BitString<T> class which takes an enumeration as the generic argument and represents a list of options wrapping the supplied enumeration. I'm hoping I can define a single Display/Editor template that handles all BitString instances.
I'm currently using Razor for my views, but I don't mind mixing and matching with ascx (or straight C# if there is a way to do it) to achieve this
Thanks
EDIT:
I think this might be a dup of this question... But it's a year and a half old, so maybe someone has a better answer at this point?
Generic partial view: how to set a generic class as model?
The problem you're describing is a fundamental principal of generics.
ICollection<Object> is not the base class of ICollection<String> even if String is a child class of Object. This is done at compile time, so you basically get two different ICollection class definitions. Therefore they cannot be casted. (Smart people of SO please feel free to correct me on any inaccuracies)
In MVC3 I have worked around this by doing the following:
class Container{
/* Stuff here */
}
class Container<T> : Container{
T Data {get;set;}
}
Then in your view
#model Container
When you need just the common stuff without knowing the generic type.
#model Container<SomeDataType>
When you need the generic type data.
Use Case:
I create a "ModelContainer" class that stores my Model inside, together with an array of Error Messages that can be displayed the page in a partial. Since the partial can be used on every page, it does not know what the Generic type will be, so this workaround is needed.
This cannot be used if you are trying to access the generic data without knowing its type. Hopefully this solves your problem.
I agree with Daryl's answer, but I would just add a small improvement.
interface IContainer{
dynamic Data {get;}
}
class Container<T> : IContainer{
T Data {get;set;}
dynamic IContainer.Data
{
get { return this.Data; }
}
}
Then in your view do the following:
#model IContainer
No, it is not possible to have views with generic type if this generic type is not known. You cannot define a model like this:
#model AppName.Models.BitString<T>
T has to be known:
#model AppName.Models.BitString<SomeEnum>
This being said I would recommend you instead of trying to reuse some models you had in your old system to think of what view models you might put in place and which will be passed to the views.
This might be less than ideal, but you should be able to use
#model BitString<dynamic>

Categories