Code
C#
public static class MyClass
{
public static object Foo = new { #class = "foo" };
public static object Barbaz = new { #class = "bar baz" };
}
cshtml
<div #MyClass.Foo></div>
<div #MyClass.Barbaz></div>
Desired HTML result
<div class="foo"></div>
<div class="foo bar"></div>
Actual HTML result
<div {="" class="foo" }=""></div>
(works, but looks wrong)
<div {="" class="bar" baz="" }=""></div>
(doesn't work, and looks even more wrong)
How should I declare Foo and Barbaz to achieve my desired result?
I would do it a little different:
C#
public static class MyClass
{
public static string Foo = "foo";
public static string Barbaz = "bar baz";
}
VIEW
<div class="#MyClass.Foo"></div>
<div class="#MyClass.Barbaz"></div>
reusing in action link:
#Html.ActionLink("mylink","#Url.Action("Index","Home")", new {#class=MyClass.Foo})
Do not use objects but strings in properties like this:
public static class MyClass
{
public static string Foo = "foo";
public static string Barbaz = "bar baz";
}
and in view set up code:
<div class = "#MyClass.Foo"></div>
<div class="#MyClass.Barbaz"></div>
Side note: you will probably need to revise your logic as setting css classes from c# backend is not something you should do very often. Try to avoid that. Instead use flags based on which you will have appropriate css classes applied in view.
class is an html attribute, IMHO, It is not a good idea to render that attribute itself from server using a variable. The value is fine.
Use simple string type which holds just the css class name
public static class MyClass
{
public static string Foo = "foo" ;
public static string Barbaz = "bar baz" ;
}
and the view
<div class="#MyClass.Foo"></div>
Your approach in the question, will work with ActionLink helper method call as the mvc framework accepts an anonymous object and build the html attributes from that. You cannot use that as it is with your normal html markup as there is nothing to convert the anonymous object to the corresponding html markup. Although you can write some code to do that, It will make your code messy. Remember , The clean approach is to write readable code (normal html markup as much as you can in the view file). This the whole reason behind the asp net team to bring the new tag helpers so designers can build the page without worrying too much about server code / any magic helper methods etc
I would lean towards avoiding to call magic helper methods in my view code. I prefer to keep it as normal HTML code as much as i can.
Related
I have a bunch of methods which manipulate the ASPX page elements and at this point it makes sense to encapsulate them into their own static object. However, it seems like I do not have access into the form elements outside of the ASPX page. Any ideas on how to go about this?
You need to pass the Page itself into the class, see the example below:
ASPX page
<form id="form1" runat="server">
<div>
<asp:TextBox ID="txtTest" runat="server" Text="Test" />
</div>
</form>
Code-Behind
protected void Page_Load(object sender, EventArgs e)
{
Process p = new Process(this);
string s = p.GetTextBoxValue();
}
Class
public class Process
{
public Page thePage { get; set; }
public Process(Page page)
{
thePage = page;
}
public string GetTextBoxValue()
{
TextBox tb = (TextBox)thePage.FindControl("txtTest");
return tb.Text;
}
}
Process is probably not the best name for the class, but this is purely a demo.
Also, passing the Page object into another class tight couples that class to the Page object. I would recommend reconsidering your design of the class you're trying to make to not rely on the Page object entirely.
If you really want to encapsulate functionality, I guess you best create a class in which you pass the relevant elements to the constructor.
If you are aiming on reuse in other pages, you could create a base page from which you inherit. Another option is to do stuff in a master page that you refer from your pages.
I think a more detailed question is required to give a more detailed answer.
You need to pass Page object as one of the parameters to your class methods, this way its elements will be accessible inside the class.
For example if you have a class like:
public class CMyDataClass {
public bool CompareText(System.Web.UI.Page i_oPage) {
TextBox oTextBox = i_oPage.FindControl("TextBox1");
return (oTextBox.Text == "My Data");
}
}
You can use it like this from the page:
CMyDataClass oMyDataClass = new CMyDataClass();
if (oMyDataClass.CompareText(this)) {
Response.Write("Ok!");
}
I'm getting ready to update a website and I'm thinking about using oocss. I know there are a lot of mixed opinions about oocss, but so far it seems like a good choice for my project. The only thing I dislike about it is the use of tags in the HTML to style complex modules. The oocss documentation says to use some sort of scripting to insert these tags, making them easier to remove down the road when they are no longer necessary, but it doesn't go into any detail how to do this. If anyone could point me to a solution or more information about how to implement this, I would appreciated it. My site is written with asp in c# and i will be using razor templates. I would also like to achieve this server-side, not with java-script, if possible. Thanks.
This is a sample of the html for a complex module:
<div class="mod complex">
<b class="top"><b class="tl"></b><b class="tr"></b></b>
<div class="inner">
<div class="bd">
<p>Lorem ipsum.</p>
</div>
</div>
<b class="bottom"><b class="bl"></b><b class="br"></b></b>
Thanks to Mark I looked around for some html helper examples and found this post.
I modified the code a little so my helper looks like:
public class Complex : IDisposable
{
private readonly ViewContext _viewContext;
private bool _disposed = false;
public Complex(ViewContext viewContext)
{
_viewContext = viewContext;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
_viewContext.Writer.Write(
#"</div>
<b class=""bottom""><b class=""bl""></b><b class=""br""></b></b>
</div>"
);
}
}
}
public static class HtmlExtensions
{
public static Complex Complex(this HtmlHelper htmlHelper)
{
htmlHelper.ViewContext.Writer.Write(
#"<div class=""mod complex"">
<b class=""top""><b class=""tl""></b><b class=""tr""></b></b>
<div class=""inner"">"
);
return new Complex(htmlHelper.ViewContext);
}
}
And then in my view I included:
#using (Html.Complex())
{
<div class="bd">
<p>Lorem ipsum.</p>
</div>
}
But now is there a way to include a css class in the view so that it is put into the first div's classes? I'm sorry if this is a stupid question but I very new to C# and MVC in general. This is what I've got so far:
I changed the static class html.extensions to this:
public static class HtmlExtensions
{
public static Flow Flow(this HtmlHelper htmlHelper, string classname)
{
htmlHelper.ViewContext.Writer.Write(#"<div class="" " + classname + #"mod complex flow"">
<b class=""top""><b class=""tl""></b><b class=""tr""></b></b>
<div class=""inner"">"
);
return new Flow(htmlHelper.ViewContext);
}
}
And I put this in my view:
#using (Html.Flow("bgtest"))
{
<div class="bd">
<div style="height:442px; width:980px; background-color: grey;"></div>
</div>
}
I get this error: CS1501: No overload for method 'Flow' takes 0 arguments.
I'm not sure if there any open-source available to help you to achieve oocss in ASP.NET MVC or Razor. I would suggest go for creating reusable html helpers that renders the modules (buttons, pagination etc.) using oocss.
For ex. to create a button using oocss. You can have a separate html helper CssButton that renders the content with the expected html with the passed model. So you can reuse anywhere in views as,
#Html.CssButton(model)
For creating custom html helpers
http://www.asp.net/mvc/tutorials/older-versions/views/creating-custom-html-helpers-cs
Even you can wrap all the html helpers that renders the modules in a separate assembly so it would be reusable across projects.
http://blogs.msdn.com/b/davidebb/archive/2010/10/27/turn-your-razor-helpers-into-reusable-libraries.aspx
I am all new with ASP.NET MVC and Extension methods.
I have created two Extensions that i want to use in my View:
public static class Extensions
{
public static string ToYesNo(this bool value)
{
return value ? "Yes" : "No";
}
public static string MonthToString(this int value)
{
return (value >= 1 && value <= 12) ? CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(value) : "";
}
}
I can use ToYesNo with a bool in the View, but i cannot view MonthToString with an integer. I get:
'int' does not contain a definition for 'MonthToString'
The Extensions are in a namespace called BitvaerkAdmin.Models, and i reference that in th cshtml file.
Why can't i use my integer extension?
Edit:
I reference the extensions in my view like this:
#using BitvaerkAdmin.Models
<h3>
#ViewBag.Month.MonthToString()
</h3>
#foreach (Order order in ViewBag.Orders)
{
<td>
#order.Valid.ToYesNo()
</td>
}
OK, now that you have shown your code it is clear why it doesn't work. You use ViewBag (the root of all evil in ASP.NET MVC and the origin of all problems that people are having - little addition from the author of this answer).
Once you borrow its path the fall to the abyss is eminent. This fall will be accelerated by the cast that you need to perform in order to make it work:
#((int)(ViewBag.Month).MonthToString())
Simply try running the following console application and you will understand that dynamic variables cannot be used to dispatch extension methods:
public static class Extensions
{
public static string MonthToString(this int value)
{
return (value >= 1 && value <= 12) ? CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(value) : "";
}
}
class Program
{
static void Main()
{
dynamic foo = 123;
Console.WriteLine(foo.MonthToString()); // crash at runtime
}
}
See why I always critique ViewBag when I see people using it? Because it leads you to all kind of strange things. You lose Intellisense, you cannot dispatch extension methods on dynamic variables, ...
So actually you don't need to cast. You shouldn't use any ViewBag/ViewData at all. You should be using strongly typed view models:
#using BitvaerkAdmin.Models
#model MyViewModel
<h3>
#Model.Month.MonthToString()
</h3>
#foreach (Order order in Model.Orders)
{
<td>
#order.Valid.ToYesNo()
</td>
}
and to avoid the foreach loop you could use display templates:
#using BitvaerkAdmin.Models
#model MyViewModel
<h3>
#Model.Month.MonthToString()
</h3>
#Html.DisplayFor(x => x.Orders)
and then define a display template for the order which will automatically be rendered by the framework for all elements of the collection (~/Views/Shared/DisplayTemplates/Order.cshtml):
#using BitvaerkAdmin.Models
#model Order
<td>
#Model.Valid.ToYesNo()
</td>
Everything is now strongly typed and working.
After giving reference of Extention class in My view I tried as below and it worked for me.
#using NameSpace Of your Extentions class;
#{
int i = 10;
}
<span>#i.MonthToString()</span>
I have few elements on my view textboxes , dropdowns etc. All of them have some unique attributes created like that
<%: Html.DropDownListFor(model => model.MyModel.MyType, EnumHelper.GetSelectList< MyType >(),new { #class = "someclass", #someattrt = "someattrt"})%>
I would like to create a read only version of my page by setting another attribute disabled.
Does anybody know how can I do it using variable that can be set globally?
Something like:
If(pageReadOnly){
isReadOnlyAttr = #disabled = "disabled";
}else
{
isReadOnlyAttr =””
}
<%: Html.DropDownListFor(model => model.MyModel.MyType, EnumHelper.GetSelectList< MyType >(),new { #class = "someclass", #someattrt = "someattrt",isReadOnlyAttr})%>
I don’t want to use JavaScript to do that
I have done something similar to what you are after I think - basically I have a couple of different users of the system and one set have read-only privileges on the website. In order to do this I have a variable on each view model:
public bool Readonly { get; set; }
which is set in my model/business logic layer depending on their role privileges.
I then created an extension to the DropDownListFor Html Helper that accepts a boolean value indicating whether the drop-down list should be read only:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Mvc.Html;
public static class DropDownListForHelper
{
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> dropdownItems, bool disabled)
{
object htmlAttributes = null;
if(disabled)
{
htmlAttributes = new {#disabled = "true"};
}
return htmlHelper.DropDownListFor<TModel, TProperty>(expression, dropdownItems, htmlAttributes);
}
}
Note that you can create other instances that take more parameters also.
Than in my view I simply imported the namespace for my html helper extension and then passed in the view model variable readonly to the DropDownListFor Html helper:
<%# Import Namespace="MvcApplication1.Helpers.HtmlHelpers" %>
<%= Html.DropDownListFor(model => model.MyDropDown, Model.MyDropDownSelectList, Model.Readonly)%>
I did the same for TextBoxFor, TextAreaFor and CheckBoxFor and they all seem to work well. Hope this helps.
Rather than disabling the drop down list, why not replace it with the selected option... if you are doing this for a lot of stuff, you should think about having a read-only view and an editable view...
<% if (Model.IsReadOnly) { %>
<%= Model.MyModel.MyType %>
<% } else { %>
<%= Html.DropDownListFor(model => model.MyModel.MyType, EnumHelper.GetSelectList< MyType >(),new { #class = "someclass", someattrt = "someattrt"})%>
<% } %>
And just as an aside, you only need to escape the attribute name with "#" if it is a reserved word, such as "class".
Update
Okay. I do have an answer for you - but on the condition that you read this stuff before you implement it.
MVC is all about separating the concerns. Putting logic in the controller that is specifically a concern of the view is an abuse of MVC. Please don't do it. Anything specific to the view, like HTML, attributes, layout - none of that should ever feature in "controllerville". The controller shouldn't have to change because you want to change something in the view.
It is really important that you understand what MVC is trying to achieve and that the following example breaks the whole pattern and puts view stuff in entirely the wrong place in your application.
The correct fix would be to have a "read" view and an "edit" view - or to put any conditional logic in the view. But here is a way of doing what you want. :(
Add this property to the Model.
public IDictionary<string, object> Attributes { get; set; }
In the controller you can conditionally set the attributes:
model.Attributes = new Dictionary<string, object>();
model.Attributes.Add(#"class", "test");
if (isDisabled) {
model.Attributes.Add("disabled", "true");
}
Use the attributes in your view:
<%= Html.TextBoxFor(model => model.SomeValue, Model.Attributes)%>
When you use Html.RenderPartial is takes the name of the view you want to render, and renders it's content in that place.
I would like to implement something similar. I would like it to take the name of the view you want to render, along with some other variables, and render the content within a container..
For example:
public static class WindowHelper
{
public static string Window(this HtmlHelper helper, string name, string viewName)
{
var sb = new StringBuilder();
sb.Append("<div id='" + name + "_Window' class='window'>");
//Add the contents of the partial view to the string builder.
sb.Append("</div>");
return sb.ToString();
}
}
Anyone know how to do this?
The RenderPartial extensions are programmed to render directly to the Response object... you can see this in the source code for them:
....).Render(viewContext, this.ViewContext.HttpContext.Response.Output);
This means that if you change your approach a little bit, you can probably accomplish what you want. Rather than appending everything to a StringBuilder, you could do something like this:
using System.Web.Mvc.Html;
public static class WindowHelper
{
public static void Window(this HtmlHelper helper, string name, string viewName)
{
var response = helper.ViewContext.HttpContext.Response;
response.Write("<div id='" + name + "_Window' class='window'>");
//Add the contents of the partial view to the string builder.
helper.RenderPartial(viewName);
response.Write("</div>");
}
}
Note that including System.Web.Mvc.Html allows you access to the RenderPartial() methods.
We are fixing this in MVC 2. You will be able to call Html.Partial() and get the actual contents of the view as a string.
Why not create a second view and have the partial inside that, pass Name as ViewData or in model etc..
Something like:
<div id='<%= ViewData["Name"] + "_Window"%>' class='window'>
<% Html.RenderPartial(ViewData["Name"]); %>
</div>
Hope that helps,
Dan