I've already set the current theme in my page,
<MudThemeProvider Theme="_currentTheme" />
#code
{
private readonly MudTheme _currentTheme = new PortalTheme();
}
What is the best way to access it from another component? Should I create a cascading property?
To elaborate on the answer of henon, here is how I use CascadingValue.
In my MainLayout.razor, where I inject custom theme into <MudThemeProvider/>
<MudThemeProvider Theme="customTheme"/>
...
<MudMainContent>
<CascadingValue Value="#customTheme">
<div class="body-container">
#Body
</div>
</CascadingValue>
</MudMainContent>
...
#code {
MudTheme customTheme = new MudTheme()
{
Palette = new Palette()
{
Primary = new MudColor("011E41")
}
};
}
And then, in any other components down the hierarchy, I can use like this
<MudText Typo="Typo.h4" Style="#($"color:{Theme.Palette.Primary}")">
Example usage of the custom theme.
</MudText>
#code {
[CascadingParameter]
protected MudTheme Theme { get; set; }
}
You can either use a CascadingValue or you create a scoped service which you inject into your components which holds a reference to the theme.
Related
#{
int i = 0;
}
#helper Text() {
<input type="text" name="Ans[#i].Text" />
}
i is not accessible in helper. How to access it?
You can simply add it as member to you page by using #functions declaration:
#functions
{
private int i;
}
You could pass it as parameter to the helper:
#helper Text(int i) {
<input type="text" name="Ans[#i].Text" />
}
and then:
#{
int i = 0;
}
#SomeHelper.Text(i)
or you could simply use editor templates which will take care of everything and get rid of those helpers. For example:
#Html.EditorFor(x => x.Ans)
You can achieve this by changing base class for your view. This scenario applies to situation where helper is declared in view.
Create a base class that inherits from WebViewPage and introduce shared field or property:
public class MyBasePage<T> : WebViewPage<T>
{
public int i;
public override void Execute()
{ }
}
Using #inherits directive change base class. And now field/property is acessible both from "page context" and helper:
#inherits NamespaceOfYourBaseClass.MyBasePage<YourModel>
#{
i = 0;
}
#helper Text() {
<input type="text" name="Ans[#i].Text" />
}
If you want to have a thing that is close to term "page property/field" but dont want to create a base class or helpers are stored within App_Code folder then you can try WebPageBase.Page property.
MSDN: Provides property-like access to page data that is shared between
pages, layout pages, and partial pages.
The code in this case would be:
#{
Page.i = 0;
}
#helper Text() {
<input type="text" name="Ans[#Page.i].Text" />
}
The drawback is that Page property is of type dynamic and thus does not support intellisense. As an alternative to Page there is another property - WebPageBase.PageData.
MSDN: Provides array-like access to page data that is shared between pages,
layout pages, and partial pages.
In this case a class-container of strings/ints keys for "page variables" could be created. And the code would be like:
// class visible to views and helpers
class MyViewFields {
public const string i = "MyViewFields.i"; // or maybe generate guid for key so there would be not doubts about its uniqueness.. but how would you debug this? :)
}
// in MyView.cshtml
#{
PageData[MyViewFields.i] = 0
}
#helper Text() {
<input type="text" name="Ans[#PageData[MyViewFields.i]].Text" />
}
This at least provides constraints for shared page data but still no control over value type.
I have the following in my project (MVC3) N2CMS 2.2.1 (from nuget). I am able to edit the main page using the "manage parts" functionality and drag an image part onto the page and set it's content. However, when it renders the page it renders my Image.cshtml file inside of the _layout.cshtml (like it would for a page, not a part)... this is causing the site to have multiple head/footer etc tags and breaking the layout. How can i get the DroppableZones to render the partials properly (without having to set Layout = "" in every partial view manually). If you would like to test this yourself, you can check out the code from https://github.com/robbihun/N2CMSBaseStarterSite run it, go through the N2CMS setup with the sqlite db and use the manage parts feature (http://screencast.com/t/w9q5a49Ei8) to drag an image part onto the home page.
_layout.cshtml
<body>
#{ Html.ControlPanel().Render(); }
#RenderBody()
<footer>© #DateTime.Now.Year</footer>
#RenderSection("scripts", false)
</body>
StartHome.cshtml
<div>
#{ Html.DroppableZone(Zones.ImageSlider).Render(); }
</div>
Image.cshtml
#model ImagePart
<div class="image">
<img src="#Model.Image" alt="#Model.Title" />
<span class="title">#Model.Title</span>
<span class="description">#Model.ShortDescription</span>
</div>
StartHomePage.cs
[PageDefinition("Start Page", Description = "The start or home page of the website.", SortOrder = 1, InstallerVisibility = InstallerHint.PreferredRootPage | InstallerHint.PreferredStartPage)]
[WithEditableTitle("Title", 1, Focus = true, ContainerName = Tabs.Content)]
[RestrictParents(typeof(IRootPage))]
public class StartHomePage : PageBase
{
[EditableFreeTextArea("Main Content", 2, ContainerName = Tabs.Content)]
public virtual string MainContent { get; set; }
}
ImagePart.cs
[PartDefinition("Image Part", Description = "Image with title and description.")]
[WithEditableTitle("Title", 10)]
public class ImagePart : PartBase
{
[FileAttachment, EditableImageUpload("Image", 5)]
public virtual string Image { get; set; }
[EditableTextBox("Short Description", 15, TextMode = TextBoxMode.MultiLine, Rows = 5, Columns = 15)]
public virtual string ShortDescription { get; set; }
}
Add a _ViewStart.cshtml in the Views/Shared/Parts folder, and it'll override the Layout setting in that folder. In the new _ViewStart, set Layout = null; and don't set the Layout in your partial views.
Here's a pull request for you: https://github.com/robbihun/N2CMSBaseStarterSite/pull/1
I am becoming more familiar with MVC 3 and the RAZOR view engine. I have a question regarding layouts and shared controls on pages.
Let’s say I have a header section defined in my main layout. In that header is a dropdown I need to populate with project names. This dropdown will serve as a context for the entire site and is present on all pages. As an example, if the user selects “Project A” from the drop down, all of the views for the site will be based on “Project A”. Since this dropdown control is rather static and is used by the entire site, where is the best place to put the code to pull all the projects to display in the dropdown? In a Partial View? In a HTML helper? Another thought is, if a user selects a new value, they would be taken to a dashboard or similar page for that newly selected project. I am trying to figure out how to reuse this control on every page in the site without having to keep wiring it up in every possible controller.
You could use a child action along with the Html.Action helper. So you start by defining a view model:
public class ProjectViewModel
{
[DisplayName("Project name")]
public string ProjectId { get; set; }
public IEnumerable<SelectListItem> ProjectNames { get; set; }
}
then a controller:
public class ProjectsController: Controller
{
private readonly IProjectsRepository _repository;
public ProjectsController(IProjectsRepository repository)
{
_repository = repository;
}
public ActionResult Index(string projectId)
{
var projects = _repository.GetProjects();
var model = new ProjectViewModel
{
ProjectId = projectId,
ProjectNames = projects.Select(x => new SelectListItem
{
Value = x.Id,
Text = x.Name
})
};
return PartialView(model);
}
}
then the corresponding view (~/views/projects/index.cshtml):
#model ProjectViewModel
#Html.LabelFor(x => x.ProjectId)
#Html.DropDownListFor(
x => x.ProjectId,
Model.ProjectNames,
new {
id = "projects",
data_url = Url.Action("SomeAction", "SomeController")
}
)
Now all that's left is to render this widget inside the _Layout.cshtml:
#Html.Action("Index", "Products", new { projectid = Request["projectId"] })
And now we could put some javascript so that when the user decides to change the selection he is redirected to some other action:
$(function() {
$('#projects').change(function() {
var url = $(this).data('url');
var projectId = encodeURIComponent($(this).val());
window.location.href = url + '?projectid=' + projectId;
});
});
Another possibility is to put the dropdown inside an HTML form:
#model ProjectViewModel
#using (Html.BeginForm("SomeAction", "SomeController", FormMethod.Get))
{
#Html.LabelFor(x => x.ProjectId)
#Html.DropDownListFor(
x => x.ProjectId,
Model.ProjectNames,
new {
id = "projects",
}
)
}
so that inside the javascript we don't have to worry about building urls when the selection changes and simply trigger the containing form submission:
$(function() {
$('#projects').change(function() {
$(this).closest('form').submit();
});
});
We just did a similiar thing on a project.
First, you can't really put it in a section because you have to put that section on every view, you could put it in a partial but you would still have to call it from every view.
Second, you can't really put it in the Layout page because the layout page isn't passed any kind of model. So I created an html helper and referenced that in the layout page. There are lots of tutorials on creating html helpers so I won't put the code here. But essentially in your html helper you can make a database call to get all of your projects. Then you can create a select list using string builder in the html helper and return that to the layout page. We then used jquery to add an on change event to the select list. When the select list changed it loaded a new page. So for example, in your select list the value of each item could be the project id, then on change it redirects them to a page like /Projects/View?id=234 where 234 is your project id.
So things to research. 1. Creating HTML Helpers 2. JQUERY change event.
That should get you in the right direction. Let me know if you need any other help and I can post some code.
I am using Knockout-JS to bind properties in my view to my view model. Knockout-JS uses a custom attribute called 'data-bind' that you have to append to controls in which you want to be bound to view model objects.
Example:
<input type='text' name='first-name' data-bind='value: firstName'/>
Notice the 'data-bind' attribute.
In my view rendering, I am having trouble rendering a textbox that has this attribute. I am aware the Html.EditorFor, Html.TextBoxFor, and Html.TextBox helpers all take an anonymous object that you can use to specify custom attributes. The only problem with this implementation is C# doesn't allow dashes as variable names, so this won't compile:
#Html.EditorFor(m => m.FirstName, new { data-bind = "value: firstName" });
The only thing I can think of is this (in view-model):
public class DataBindingInput
{
public string Value { get; set; }
public string DataBindingAttributes { get; set }
}
public class MyViewModel
{
...
public DataBindingValue firstName { get; set; }
....
}
And a view template called "DataBindingInput.cshtml":
#model DataBindingInput
<input type='text' data-binding='#Model.DataBindingAttributes' value='#Model.Value'>
The only trouble with this is I lose the automatic generation of the input name so it won't work on a post-back because the model binder has no idea how to bind it.
How can I make this work?
Thanks to Crescent Fish above, looks like you can just use underscores and MVC 3 will convert them to dashes since underscores aren't allowed in HTML attribute names.
One again Microsoft poor documentation has left me confused. I am trying to use the new features of the .NET 4.0 framework. I am using the following code to populate the Title and Director but it keeps getting blank.
The service returns the result correctly like
[d: { title = "ss, director ="" } something like that but the li never gets populated.
<script language="javascript" type="text/javascript">
Sys.require([Sys.components.dataView, Sys.components.dataContext,Sys.scripts.WebServices], function () {
Sys.create.dataView("#moviesView",
{
dataProvider: "MovieService.svc",
fetchOperation: "GetMovies",
autoFetch: true
});
});
</script>
And here it the HTML code:
<ul id="moviesView">
<li>
{{Title}} - {{Director}}
</li>
</ul>
IS THIS THE LATEST URL TO Start.js file.
<script src="http://ajax.microsoft.com/ajax/beta/0911/Start.js"></script>
Here is the Ajax-Enabled WCF Service:
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MovieService
{
[OperationContract]
public Movie GetMovies()
{
return new Movie() { Title = "SS", Director = "SSSSS" };
}
}
[DataContract]
public class Movie
{
[DataMember]
public string Title { get; set; }
[DataMember]
public string Director { get; set; }
}
You need to add the sys-template class attribute to the unordered list tag.
<ul id="moviesView" class="sys-template">
Here's an excerpt from Client-side Data Binding in ASP.NET AJAX 4.0
The one other requirement for defining
a template is the parent element must
have the sys-template CSS class
applied, and that class must be
defined with display set to none, as
shown in the example above. This
convention serves two purposes – it
helps the parser identify which
elements are part of a template on
your page (which will become important
when we use declarative
instantiation), and it keeps the
template markup hidden until ASP.NET
Ajax has completed the binding (it
will toggle the display to be
visible).