I know that i can get all the registered views in a region with :
var vs = mRegionManager.Regions[RegionNames.MainRegionStatic].Views.ToList();
and i can see there is the following code :
mRegionManager.Regions[RegionNames.MainRegionStatic].ActiveViews
which is giving a list of Active View, but I'm having my region attached to a ContentControl which always has a single ActiveView. Am i misunderstood or is there a way to get the single active view?
var singleView = regionManager.Regions["MyRegion"].ActiveViews.FirstOrDefault();
var singleView = regionManager.Regions["MyRegion"].ActiveViews.FirstOrDefault();
This is not correct, as it will just bring whatever view that got activated first. not the currently active/visible view.
Can't find a direct solution though, that doesn't involve custom implementation on View or ViewModel.
Well, you could use the NavigationService Journal. It takes record of all the navigation that takes place in your application. So, you can get the name of the view like this:
string name = mRegionManager.Regions[RegionNames.MainRegionStatic].NavigationService.Journal.CurrentEntry.Uri;
Then you can get the view like this:
mRegionManager.Regions[RegionNames.MainRegionStatic].GetView(name);
Sweet Right? :)
Using Xamarin.Forms & Prism, this works well for me. Change the regionName, change the View Model base to yours.
public VisualElement GetCurrentRegionView()
{
var regionName = "MenuPageRegion";
var region = RegionManager.Regions[regionName];
var uri = region.NavigationService.Journal.CurrentEntry.Uri.OriginalString;
foreach (var view in region.ActiveViews)
{
var name = view.GetType().FullName;
if (name.EndsWith(uri))
return view;
}
return null;
}
public CommonViewModelBase GetCurrentRegionViewModel()
{
var view = GetCurrentRegionView();
if (view != null)
{
var binding = view.BindingContext;
if (binding is CommonViewModelBase model)
return model;
}
return null;
}
Related
I want to access the layout definition of an item so that I can access the renderings added to the item, and then access the datasources attached to said renderings. I can't seem to find a way to do this. The best I could do is access the __renderings field but I then found out that this is going to access the original rendering definition item rather than the specific, datasourced instance stored in the Design Layout.
This is on Sitecore 7.5 MVC
If it helps, this is what I tried doing:
// Get the default device
DeviceRecords devices = item.Database.Resources.Devices;
DeviceItem defaultDevice = devices.GetAll().Where(d => d.Name.ToLower() == "default").First();
// Get the rendering references for the default device
Sitecore.Data.Fields.LayoutField layoutField = item.Fields["__renderings"];
Sitecore.Layouts.RenderingReference[] renderings = layoutField.GetReferences(defaultDevice);
// Get the required renderings
RenderingItem headerRenderingItem = null;
RenderingItem aboutRenderingItem = null;
foreach (var rendering in renderings)
{
if (rendering.Placeholder == "headerPlaceholder")
headerRenderingItem = rendering.RenderingItem;
else if (rendering.Placeholder == "/aboutSectionPlaceholder/textPlaceholder")
aboutRenderingItem = rendering.RenderingItem;
}
Assert.IsNotNull(headerRenderingItem, "no header rendering item found");
Assert.IsNotNull(aboutRenderingItem, "no about rendering item found");
// Get their datasources
ID headerDatasourceId = ID.Parse(headerRenderingItem.DataSource); // The datasource string is null as it is accessing the datasource definition item itself
ID aboutDatasourceId = ID.Parse(aboutRenderingItem.DataSource); // Same as above
The RenderingReference.RenderingItem refers to the rendering item in the /layout section. What you could do is use RenderingReference.Settings.Datasource.
So your code would look something like:
foreach (var rendering in renderings)
{
if (rendering.Placeholder == "headerPlaceholder")
headerRenderingDatasourceId = rendering.Settings.Datasource;
else if (rendering.Placeholder == "/aboutSectionPlaceholder/textPlaceholder")
aboutRenderingDatasourceId = rendering.Settings.Datasource;
}
Item headerRenderingDatasourceItem;
if (!string.IsNullOrEmpty(headerRenderingDatasourceId)
headerRenderingDatasourceItem = item.Database.GetItem(headerRenderingDatasourceId);
In my mvc solution I was originally using a viewModel to hold an IEnumerable of SelectListItems. These would be used to populate a dropdownfor element like below
#Html.DropDownListFor(model => model.Type, Model.PrimaryTypeList, new { data_acc_type = "account", data_old = Model.Type, #class = "js-primary-account-type" })
the problem being that whenever I had to return this view, the list would need re-populating with something pretty heavy like the following:
if(!ModelState.IsValid){
using (var typeRepo = new AccountTypeRepository())
{
var primTypes = typeRepo.GetAccountTypes();
var primtype = primTypes.SingleOrDefault(type => type.Text == model.Type);
model.PrimaryTypeList =
primTypes
.Select(type => new SelectListItem()
{
Value = type.Text,
Text = type.Text
}).ToList();
}
return View(model);
}
It seemed silly to me to have to rewrite - or even re-call (if put into a method) the same code every postback. - the same applies for the ViewBag as i have about 6 controllers that call this same view due to inheritance and the layout of my page.
At the moment i'm opting to put the call actually in my razor. but this feels wrong and more like old-school asp. like below
#{
ViewBag.Title = "Edit Account " + Model.Name;
List<SelectListItem> primaryTypes = null;
using (var typeRepo = new AccountTypeRepository())
{
primaryTypes =
typeRepo.GetAccountTypes()
.Select(t => new SelectListItem()
{
Value = t.Text,
Text = t.Text
}).ToList();
}
#Html.DropDownListFor(model => model.Type, primaryTypes, new { data_acc_type = "account", data_old = Model.Type, #class = "js-primary-account-type" })
Without using something completely bizarre. would there be a better way to go about this situation?
UPDATE: While semi-taking onboard the answer from #Dawood Awan below. my code is somewhat better, still in the view though and i'm 100% still open to other peoples ideas or answers.
Current code (Razor and Controller)
public static List<SelectListItem> GetPrimaryListItems(List<AccountType> types)
{
return types.Select(t => new SelectListItem() { Text = t.Text, Value = t.Text }).ToList();
}
public static List<SelectListItem> GetSecondaryListItems(AccountType type)
{
return type == null?new List<SelectListItem>(): type.AccountSubTypes.Select(t => new SelectListItem() { Text = t.Text, Value = t.Text }).ToList();
}
#{
ViewBag.Title = "Add New Account";
List<SelectListItem> secondaryTypes = null;
List<SelectListItem> primaryTypes = null;
using (var typeRepo = new AccountTypeRepository())
{
var primTypes = typeRepo.GetAccountTypes();
primaryTypes = AccountController.GetPrimaryListItems(primTypes);
secondaryTypes = AccountController.GetSecondaryListItems(primTypes.SingleOrDefault(t => t.Text == Model.Type));
}
}
In practice, you need to analyse where you app is running slow and speed up those parts first.
For starters, take any code like that out of the view and put it back in the controller. The overhead of using a ViewModel is negligible (speed-wise). Better to have all decision/data-fetching code in the controller and not pollute the view (Views should only know how to render a particular "shape" of data, not where it comes from).
Your "Something pretty heavy" comment is pretty arbitary. If that query was, for instance, running across the 1Gb connections on an Azure hosted website, you would not notice or care that much. Database caching would kick in too to give it a boost.
Having said that, this really is just a caching issue and deciding where to cache it. If the data is common to all users, a static property (e.g. in the controller, or stored globally) will provide fast in-memory reuse of that static list.
If the data changes frequently, you will need to provide for refreshing that in-memory cache.
If you used IOC/injection you can specific a single static instance shared across all requests.
Don't use per-session data to store static information. That will slow down the system and run you out of memory with loads of users (i.e. it will not scale well).
If the DropDown Values don't change it is better to save in Session[""], then you can access in you View, controller etc.
Create a class in a Helpers Folder:
public class CommonDropDown
{
public string key = "DropDown";
public List<SelectListItem> myDropDownItems
{
get { return HttpContext.Current.Session[key] == null ? GetDropDown() : (List<SelectListItem>)HttpContext.Current.Session[key]; }
set { HttpContext.Current.Session[key] = value; }
}
public List<SelectListItem> GetDropDown()
{
// Implement Dropdown Logic here
// And set like this:
this.myDropDownItems = DropdownValues;
}
}
Create a Partial View in Shared Folder ("_dropDown.cshtml"):
With something like this:
#{
// Add Reference to this Folder
var items = Helpers.CommonDropDown.myDropDownItems;
}
#Html.DropDownList("ITems", items, "Select")
And then at the top of each page:
#Html.Partial("_dropDown.cshtml")
We are using a hybrid razor engine to create components in Sitecore.
I need to get the rendering definition id, but i am not able to get it.
This is our baseComponent class that inherits from Sitecore.Web.UI.WebControl
protected override void DoRender(HtmlTextWriter output)
{
string razorpath = RazorPath;
Model = GetViewModel();
var itemId = ID; //renderingID || getitem() also not working
HttpContext.Current.Response.Write(itemId);
Guid itemGuid;
if (Guid.TryParse(itemId, out itemGuid))
{
var webControl = ContentStore.GetItem<WebControl>(itemId);
if (webControl != null && string.IsNullOrEmpty(webControl.View))
razorpath = webControl.View.Replace("/WebControls", "");
}
var renderedContent = TemplateExpander.ExpandTemplate(razorpath, Model);
output.Write(renderedContent);
}
Not entirely sure what you're after, but since you're inheriting from Sitecore.Web.UI.WebControl, it might be something like this you're after:
var p = Parent as Sublayout;
var rId = p.RenderingID;
I'm making a portfolio. In my Portfolio view, I have the following code:
Learn More
It's placed within a loop that goes through the ViewBag and draws a button for each "Project" in the "Project Container". When clicked, this should lead to the project page and display the relevant information.
When written as above (calling the Index() method in the Project controller) everything works as intended and the page displays thusly:
http://gyazo.com/57901eab7ccf8be45270312a92880072
However, when I call another method in my controller, which I have named "SetProject", the following happens:
http://gyazo.com/6a5c6164dc4e8b55ec8a6684ce469652
My controller methods look like the following:
public ActionResult Index(string _title, string _imagePath, string _brief, string _description)
{
ProjectViewModel proj = new ProjectViewModel { Title = _title, ImagePath = _imagePath, Brief = _brief, FullDescription = _description };
return View("Project", proj);
}
public ActionResult SetProject(string _title, string _imagePath, string _brief, string _description)
{
ProjectViewModel proj = new ProjectViewModel { Title = _title, ImagePath = _imagePath, Brief = _brief, FullDescription = _description };
return View("Project", proj);
}
You might noticed that these are identical - hence my question, WHY do they look so different? Why does the second simply not work?
I'm quite new to ASP.NET and the Razor view engine and trying to learn, but got very stuck here. Would appreciate any and all help.
Could this be something to do with the Route config? The only difference I can see is the URL being different for the broken one (having the extra "/SetProject")
Why does the second simply not work?
Because of your css references.It is working actualy just it doesn't look like what you expected. Make sure you include your all css references inside of Layout and make sure your Project View using your Layout page.
I have two different pages, from which a user can click on a 'details' link and go to the details page.
On the details page, I have a 'back' button, which leads the user to the originating page, being one of the two original pages of course.
There is also one extra issue: in one of the return links, I must specify an extra anonymous object.
my view code right now is:
#{
MvcHtmlString backLink = null;
if (Model.ReturnPage == MatchResultReturnPage.Search)
{
backLink = Html.ActionLink("GoBack", "Search", new {search = true});
}
else
{
backLink = Html.ActionLink("GoBack", "Dashboard");
}
}
In the controller I now look in the url.referrer if it contains 'dashboard', then I set the Model.ReturnPage to 'Dashboard'.
Is there a cleaner way of doing this?
Put the ReturnLink as a property on your model and set it inside the controller, which will alleviate the need for you to put that logic in the view.
There are certainly cleaner ways, but as your code is currently, it is very easy to understand what you are trying to do.
I would say keep it as is and simply put a #region wrapper around it and hide it when you don't need to work with it:
#region get referrer page
MvcHtmlString backLink = null;
if (Model.ReturnPage == MatchResultReturnPage.Search)
{
backLink = Html.ActionLink("GoBack", "Search", new {search = true});
}
else
{
backLink = Html.ActionLink("GoBack", "Dashboard");
}
#region
The only thing I would suggest is to have this check in the Controller, rather than the view and simply putting the result of your check either in model property, or in the ViewBag.
To gain access to Helpers in your controller, do the following:
var URL = new UrlHelper(this.Request.RequestContext).Action("MyAction", "MyController", new { id = 123 });
you should probably implement the Back button entirely in JavaScript.
using the history object
<a href=”javascript:history.back()”> [Back]</a>