In MVC, there's a ViewSwitcher, and you can add _Layout, _Layout.mobile; MyView and optional MyView.mobile
What's the best way to accomplish this in ServiceStack razor view? Thanks
ServiceStack doesn't implicitly switch layouts at runtime, instead the preferred layout needs to be explicitly set. ServiceStack's RazorRockstars Demo website explains how to dynamically switch views, i.e:
Change Views and Layout templates at runtime
The above convention is overrideable where you can change both what View and Layout Template is used at runtime by returning your Response inside a decorated HttpResult:
return new HttpResult(dto) {
View = {viewName},
Template = {layoutName},
};
This is useful whenever you want to display the same page in specialized Mobile and Print Preview website templates. You can also let the client change what View and Template gets used by attributing your service with the ClientCanSwapTemplates Request Filter Attribute:
[ClientCanSwapTemplates]
public class RockstarsService : RestServiceBase { ... }
Which itself is a very simple implementation that also shows you can you can swap the View or Template used inside a Request Filter:
public class ClientCanSwapTemplatesAttribute : RequestFilterAttribute
{
public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
req.Items["View"] = req.GetParam("View");
req.Items["Template"] = req.GetParam("Template");
}
}
This attribute allows the client to change what View gets used with the View and Template QueryString or FormData Request Params. A live example of this feature is used to change the /rockstars page:
/rockstars?View=AngularJS
/rockstars?Template=SimpleLayout
/rockstars?View=AngularJS&Template=SimpleLayout
Changing Layout used from inside a view
You can even change the layout used by setting the Layout property from inside a Razor View, e.g:
#inherits ViewPage<Response>
#{
Layout = IsMobileRequest(base.Request) ? "_LayoutMobile" : "_Layout";
}
Related
I'm using EPiServer version 11 and I have requirement that when property of type linkItemCollection is rendered using PropertyFor() method, I need to add some custom attribute ( based on condition if target is blank ) to generated hyperlink.
#Html.PropertyFor(x => x.Layout.LinksCollection)
I have idea of creating a custom view under DisplayTemplates in view and adding new view. My query is how can i get default template for linkItemCollection to get it started ?
The easy option would be to o it yourself and not worry about the Property for, the only slight issue is that you may not get inline editing to work.
https://www.jondjones.com/learn-episerver-cms/episerver-developers-tutorials/episerver-properties/how-to-display-a-list-of-links-in-episerver/
To go with your route
[UIHint("MyView")]
[Display(
GroupName = SystemTabNames.Settings,
Order = 100)]
public virtual LinkItemCollection MyProperty{ get; set; }
In Views/Shared/DisplayTemplates add a template MyView.cshtml
Instead of using the PropertyFor you could take full control of the rending yourself.
// FullRefreshPropertiesMetaData asks on-page edit to reload the page
// to run the following custom rendering again after the value has changed.
#Html.FullRefreshPropertiesMetaData(new []{ "RelatedContentLinks" })
// EditAttributes enables on page-edit when you have custom rendering.
<p #Html.EditAttributes(m => m.CurrentPage.RelatedContentLinks) >
#if (Model.CurrentPage.RelatedContentLinks != null)
{
<span>See also:</span>
foreach (LinkItem item in Model.CurrentPage.RelatedContentLinks)
{
#item.Text }
}
</p>
Taken from the EPi documentation
Thanks for your input on this.
I managed to resolve this as below.
public static MvcHtmlString LinkItemCollectionFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
MvcHtmlString result = PropertyExtensions.PropertyFor(html, expression);
return MvcHtmlString.Create(result.ToString().Replace("target=\"_blank\"", "target=\"_blank\" rel=\"noopener noreferrer\""));
}
Hope, it helps someone.
I'm planning to create some pages which display data from an external SQL Server with Orchard CMS. It looks like I need to write a new module to implement this function. Is there any example or idea to implement this requirement?
Yes, you will need to write a new module, which provides a new content part and a content part driver. That driver will be responsible for fetching the data from the external SQL Server, which you will set to a property on the shape you will be returning from your driver. The shape's view will then display your data.
This tutorial will walk you through writing a custom content part: http://docs.orchardproject.net/en/latest/Documentation/Writing-a-content-part/
When you do, make sure not to create the content part Record type, since you will not be storing and loading data from the Orchard database - you want to load data from an external database. These are the steps you should follow:
Create a new module
Create a content part class
Have your part inherit from ContentPart, not ContentPart<TRecord> since there won't be any "TRecord".
Create a content part driver
On the Display method, return a shape by calling the ContentShape method. Make sure to add the SQL data access logic within the lambda. If you do it outside of that lambda, that data access code will be invoked every time the content item using your content part is invoked. Although that sounds as if that is exactly what you want, there's a subtlety here that involves Placement.info, which you can use to determine when your shape will actually be rendered or not. If the placement logic determines that your shape should not be rendered, then you don't want to access your external data for nothing.
Create a Placement.info file to configure the shape's placement (within the context of the content item being rendered).
Create the Razor view for the shape that you return in step 3.2.
Create a Migrations class that will define your custom content part, and any content types to which you want to add your part to. See http://docs.orchardproject.net/en/latest/Documentation/Understanding-data-access/ for more information on how to create migrations.
PS. Instead of implementing your data access code directly in the driver, I recommend you implement that in a separate class. Because you know, separation of concerns and such. You can then inject that service into your driver. To have your service class be registered with the service container, make sure that you define an interface for it, that itself derives from IDependency.
Some sample pseudo code:
Service code:
public interface IMyExternalDataStore : IDependency {
IList<MyExternalDataRecord> GetMyData();
}
public class MyExternalDataStore : IMyExternalDataStore {
public IList<MyExternalDataRecord> GetMyData() {
// Connect to your SQL Server database, perhaps using EF, load the data and return it. Could of course also be simply a DataSet.
}
}
Content Part:
public class MyExternalDataPart : ContentPart {
// Nothing here, unless you want to include some properties here that influence the data that you want to load. If so, you'll also want to implement the Editor methods in your content part driver, but I'm keeping it simple.
}
Content Part Driver:
public class MyExternalDataPartDriver : ContentPartDriver<MyExternalContentPart> {
private readonly IMyExternalDataStore _dataStore;
public MyExternalDataPartDriver(IMyExternalDataStore dataStore) {
_dataStore = dataStore;
}
protected override DriverResult Display(SlideShowProPart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_MyExternalData", () => {
// Notice that we're performing the data access here within the lambda (the so called "shape factory method").
var data = _dataStore.GetMyData();
// Notice that I'm creating a property called "MyData"on the shape (which is a dynamic object).
return shapeHelper.Parts_MyExternalData(MyData: data));
}
}
}
Razor view for the Parts_MyExternalData shape:
Filename: Parts.MyExternalData.cshtml
#{
var records = (IList<MyExternalDataRecord>)Model.MyData;
}
<ul>
#foreach(var record in records) {
<li>#record.ToString()</li>
}
</ul>
Placement.info:
<Placement>
<Place Parts_MyExternalData="Content:0"/>
</Placement>
Migrations:
public class Migrations : DataMigrationImpl {
public int Create() {
// Define your content part so that you can attach it to any content type from the UI.
ContentDefinitionManager.AlterPartDefinition("MyExternalDataPart", part => part.Attachable());
// Optionally, define a new content type here programmatically or attach it to an existing type.
return 1;
}
}
The project I am working on is a mobile .NET CF based application. I have to implement the MVP pattern in it. I am now using OpenNETCF.IoC library and Services in it.
I have to refactor Windows Forms code to SmartParts.
I have a problem in implementing navigation scenario:
// Show Main menu
bodyWorkspace.Show(mainMenuView);
// Show First view based on user choice
bodyWorkspace.Show(firstView);
// In first view are some value(s) entered and these values should be passed to the second view
bodyWorkspace.Show(secondView); // How?
In Windows Forms logic this is implemented with variables:
var secondForm = new SecondForm();
secondForm.MyFormParameter = myFormParameter;
How can I reimplement this logic in MVP terms?
It greatly depends on your architecture, but this would be my suggestion:
First, ViewB does not need information in ViewA. It needs information either in the Model or a Presenter. ViewA and ViewB should get their info from the same place.
This could be done, as an example, by a service. This could look like this:
class ParameterService
{
public int MyParameter { get; set; }
}
class ViewA
{
void Foo()
{
// could also be done via injection - this is just a simple example
var svc = RootWorkItem.Services.Get<ParameterService>();
svc.MyParameter = 42;
}
}
class ViewB
{
void Bar()
{
// could also be done via injection - this is just a simple example
var svc = RootWorkItem.Services.Get<ParameterService>();
theParameter = svc.MyParameter;
}
}
Event Aggregation, also supported in the IoC framework you're using, could also work, where ViewA publishes an event that ViewB subscribes to. An example of this can be found here, but generally speaking you'll use the EventPublication and EventSubscription attributes (the former on an event in ViewA, the latter on a method in ViewB).
I have a page where you basically select a set of options (configuration), and then you go to a next page, where you do some stuff
Using the MVVM Light toolkit, I have a viewmodel that binds to the view of the first page. when the user hits a button, it redirects to another view, which would be the 2nd page
i.e.:
Page2Command = new DelegateCommand((obj) =>
Messenger.Default.Send<Uri>(new Uri("/DoStuffView.xaml", UriKind.Relative),
Common.CommonResources.GoToDoStuffRequest)) });
The problem is, the viewmodel for the 2nd view (the way that I see it) has a couple of parameters in the constructor, which are basically the dependencies on the configuration that was set on the first page.
i.e. :
public DoStuffViewModel(ICollection<Note> availableNotes, SoundMappers soundType)
{
}
The problem lies here.. How can I instantiate the viewmodel with this data that was dynamically selected by the user on the 1st page?.
I can't use the ViewModelLocator pattern that MVVM light provides, since those viewmodels don't have any dependencies, they are just by themselves (or they can retrieve data from a db, file or whatever, but they don't have any dynamic input data). I could do it through the view's constructor, instantiate there the viewmodel, and assign to the view's DataSource the newly created viewmodel, but I think that's not very nice to do.
suggestions?
As I see you send messsage using Messenger class so you are familiar with messaging in MVVM light. You have to define your own message type that should accept your parameters from page 1:
public class Page2ViewModelCreateMessage : MessageBase
{
public ICollection<Note> AvailableNotes{get;set;}
public SoundMappers SoundType{get;set;}
public Page2ViewModelCreateMessage ()
{
}
public Page2ViewModelCreateMessage(ICollection<Note> availableNotes, SoundMappers soundType)
{
this.AvailableNotes = availableNotes;
this.SoundType = soundType;
}
}
You have to send an Page2ViewModelCreateMessage instance with you parameters and send it on navigating:
var message = new Page2ViewModelCreateMessage(myAvailableNotes, mySoundType)
Messenger.Default.Send(message);
On Page2 you have to register for recieving message of type Page2ViewModelCreateMessage:
Messenger.Default.Register<Page2ViewModelCreateMessage>(this, OnPage2ViewModelCreateMessage);
..
public void OnPage2ViewModelCreateMessage(Page2ViewModelCreateMessage message)
{
var page2ViewModel = new Page2ViewModel(messsage.AvailableNotes, message.SoundType);
}
As you can see I have replace your DoStuffViewModel with Page2ViewModel to be more clear.
I hope this will help you.
NOTE:I dont guarantee that code will work as its written in notepad.
The way I do this is to have a central controller class that the ViewModels all know about, via an interface. I then set state into this before having the phone perform the navigation for me. Each ViewModel then interrogates this central class for the state it needs.
There are a number of benefits to this for me:
It allows me to have non-static ViewModels.
I can use Ninject to inject the concrete implementation of the controller class and have it scoped as a singleton.
Most importantly, when tombstoning, I only need to grab the current ViewModel and the controller class.
I ran into a problem with messaging where my ViewModel was the registered listener, because I was View First and not ViewModel First, I was forced to use static ViewModel references. Otherwise the ViewModel wasn't created in time to receive the message.
I use the controller class in conjunction with messages (it is basically the recipient of all messages around the UI) so in future if I refactor, I don't need to change much, just the recipients of the messages.
Come to think of it, the controller class is also my navigation sink - as I have some custom navigation code that skips back paging on certain pages etc.
Here's an example of my current set up:
public interface IController
{
Foo SelectedFoo { get; }
}
public class ViewModel
{
private IController _controller;
public ViewModel(IController controller)
{
_controller = controller;
}
private void LoadData()
{
// Using selected foo, we load the bars.
var bars = LoadBars(_controller.SelectedFoo);
}
}
You could use PhoneApplicationService dictionary to save data you need when navigation from first event, and parse it when you navigateTo second page. you can also use that data in your ViewModels.
Something like this:
PhoneApplicationService.Current.State["DatatFromFirstPage"] = data;
and when navigating to second page:
if (PhoneApplicationService.Current.State.ContainsKey("DatatFromFirstPage"))
{
var dataUsedOnSeconPage= PhoneApplicationService.Current.State["DatatFromFirstPage"];
}
you can use this data globally in entire app
I am working on a PRISM application where we drill down into the data (to get more details).
In my implementation I have a nested MVVM and when I navigate down the tree I would like to pass a model to a my newly created view.
As far as I know, currently PRISM allows to pass strings, but doesn't allow to pass objects. I would like to know what are the ways of overcoming this issue.
i usually use a service where i register the objects i want to be passed with a guid. these get stored in a hashtable and when navigating in prism i pass the guid as a parameter which can then be used to retrieve the object.
hope this makes sense to you!
I would use the OnNavigatedTo and OnNavigatedFrom methods to pass on the objects using the NavigationContext.
First derive the viewmodel from INavigationAware interface -
public class MyViewModel : INavigationAware
{ ...
You can then implement OnNavigatedFrom and set the object you want to pass as navigation context as follows -
void INavigationAware.OnNavigatedFrom(NavigationContext navigationContext)
{
SharedData data = new SharedData();
...
navigationContext.NavigationService.Region.Context = data;
}
and when you want to receive the data, add the following piece of code in the second view model -
void INavigationAware.OnNavigatedTo(NavigationContext navigationContext)
{
if (navigationContext.NavigationService.Region.Context != null)
{
if (navigationContext.NavigationService.Region.Context is SharedData)
{
SharedData data = (SharedData)navigationContext.NavigationService.Region.Context;
...
}
}
}
ps. mark this as answer if this helps.
PRISM supports supplying parameters:
var para = new NavigationParameters { { "SearchResult", result } };
_regionManager.RequestNavigate(ShellRegions.DockedRight, typeof(UI.SearchResultView).FullName, OnNavigationCompleted, para);
and implement the INavigationAware interface on your View, ViewModel or both.
you can also find details here: https://msdn.microsoft.com/en-us/library/gg430861%28v=pandp.40%29.aspx