In our previous article, we discussed how to use Modelmetadata to achieve internationalized resource file access, but also left some questions about how to use Modelmetadata to implement a personalized resource display of the same type of attribute information. I did not find the right plan, looking forward to the expert's guidance.
This chapter describes the third resource access scenario that is used to address the above problem, which is not a design-based approach to solving the problem.
First of all, describe our problem.
The first step is to add the properties of the two address types to the UserProfile type:
1 #regionUser address information2 3 Public int? Useraddressid {Get;Set; }4 5[ForeignKey ("Useraddressid")]6 PublicAddress useraddress {Get;Set; }7 8 #endregion9 Ten One #regionUser Company address information A - Public int? Companyaddressid {Get;Set; } - the[ForeignKey ("Companyaddressid")] - PublicAddress companyaddress {Get;Set; } - - #endregionView Code
The second step is to show the city information for these two properties in the Edituser view:
1@using (Html.BeginForm ("Edituser"," Account", FormMethod.Post))2 {3 @Html. ValidationSummary ()4<div>5@Html. labelfor (P =p.username)6@Html. textboxfor (P =p.username)7 8@Html. labelfor (P =p.useraddress.city)9@Html. textboxfor (P =p.useraddress.city)Ten One@Html. labelfor (P =p.companyaddress.city) A@Html. textboxfor (P =p.companyaddress.city) -</div> -<input type="Submit"Value="Submit"/> the}
The third step is to add the appropriate resources to the resource file:
1 <ResourceKey= "Userprofile.useraddress"value= "User Address"/>2 <ResourceKey= "UserProfile.UserAddress.City"value= "User City"/>3 <ResourceKey= "Userprofile.companyaddress"value= "Company Address"/>4 <ResourceKey= "UserProfile.CompanyAddress.City"value= " Company City"/>
Fourth step, use custom Modelmetadataprovider bindings to display the information: refer to the previous blog post.
Run the project open page and see the following:
, the custom resource information is not displayed on the page as intended. This is because in provider, the container at this time is not a strong type for the current page binding, but is the type of the holder of the current property.
As shown above, the constructed resource key value is "address.city" (we will get the same result for these two properties), so we do not have the information we expected to be displayed. Sadly, although we can see the source of the property currently accessed by looking at the Modelaccessor parameter, we still can't get this source at run time (at least I haven't found a way).
Is that it bothered me for a long time, obviously can see, but is unable to obtain. In a different way, use the HtmlHelper extension method.
The extension method is a good thing, it was introduced after Framework3. It can be achieved everywhere, and it is often used. For example, filtering the collection using Collection.where, or sorting by order, is an extension method that uses LINQ. Its benefits are obvious, and it is possible to extend the functionality directly to the original type without using inheritance. OK, let's see how we do it.
First, you need a static class, a static method (the extension method is to define a static method in a static class that will need to extend the type as an argument and declare it as an extension using the This keyword).
1 namespace Mvcapp.helpers2 {3 4 Public static Class Htmlhelperext5 {6public static mvchtmlstring Labelforproperty<TModel, TValue>(This HtmlHelper<TModel>HTML, Expression<Func<tmodel, TValue>> Expression)7 {8 return Labelforproperty (HTML, expression, NULL);9 }Ten Onepublic static mvchtmlstring Labelforproperty<TModel, TValue>(This HtmlHelper<TModel>HTML, Expression<Func<tmodel, TValue>> Expression, IDictionary<string, Object>htmlattributes) A { - string navpath = expression. Body.tostring (). TrimStart (expression. Parameters.first (). ToString (). ToArray ()). TrimStart ('. '); - string res = Resource.getdisplay (string. Format ("{0}.{ 1} ", typeof (TModel). Name, Navpath)); the return Labelextensions.label (HTML, Navpath, res?? Navpath, Htmlattributes); - } -public static mvchtmlstring Labelforproperty<TModel, TValue>(This HtmlHelper<TModel>HTML, Expression<Func<tmodel, TValue>> Expression, Object htmlattributes) - { + string navpath = expression. Body.tostring (). TrimStart (expression. Parameters.first (). ToString (). ToArray ()). TrimStart ('. '); - string res = Resource.getdisplay (string. Format ("{0}.{ 1} ", typeof (TModel). Name, Navpath)); + return Labelextensions.label (HTML, Navpath, res?? Navpath, Htmlattributes); A } at } -}View Code
In the code example above, we implemented three extension methods that correspond to the original method:
Labelfor<tmodel, tvalue> (this htmlhelper<tmodel> html, Expression<func<tmodel, TValue>> expression);
Labelfor<tmodel, tvalue> (this htmlhelper<tmodel> html, Expression<func<tmodel, TValue>> Expression, idictionary<string, object> htmlattributes);
Labelfor<tmodel, tvalue> (this htmlhelper<tmodel> html, Expression<func<tmodel, TValue>> Expression, object htmlattributes);
When implementing an extension method, we first pass expression. Body.tostring () Gets the path of the current property and, for P=>p.useraddress.city, returns its string form "p.useraddress.city". Then, replacing the formal parameter expression p in the string with the name of the current type (that is, the type of the page binding), the string "UserProfile.UserAddress.City" is generated. This string conforms to our format for the resource key value definition: "type name. Attribute Name" ( in the second example, the attribute name is actually "useraddress.city", not "City"). When getting the key to this resource, the resource accessor gets the contents of the resources, calling the Labelextensions type of label (this htmlhelper HTML, string expression, String LabelText, Idictionary<string, Object> htmlattributes) method, constructs a label tag. Finally, we need to import the namespace of the extension method implementation so that these extension methods can be used in the razor view. Simply add the namespace of the extension implementation to the Web. config (instead of the root directory) file under the Views folder:
1 <System.web.webPages.razor>2 <HostFactorytype= "System.Web.Mvc.MvcWebRazorHostFactory, SYSTEM.WEB.MVC, version=4.0.0.0, Culture=neutral, publickeytoken= 31bf3856ad364e35 " />3 <pagesPageBaseType= "System.Web.Mvc.WebViewPage">4 <Namespaces>5 ......Ten <Addnamespace= "Mvcapp.helpers" /> One </Namespaces> A </pages> - </System.web.webPages.razor>
Now that all the preparation has been done, then modify our view to use the new extension method:
1 @using (Html.BeginForm ("Edituser", "account", FormMethod.Post))2 {3 @Html. ValidationSummary ()4 <Div>5 @Html. Labelforproperty (p = p.username)6 @Html. textboxfor (p = p.username)7 8 @Html. Labelforproperty (p = p.useraddress.city)9 @Html. textboxfor (p = p.useraddress.city)Ten One @Html. Labelforproperty (p = p.companyaddress.city) A @Html. textboxfor (p = p.companyaddress.city) - </Div> - <inputtype= "Submit"value= "Submit" /> the}View Code
The Run page has the following results:
It is worth mentioning that the scheme has no conflict with Modelmetadata because the extension has not called Modelmetadataprovider. In other words, these two schemes can coexist. When there are multiple properties of the same type in our page model type that need to be displayed in the interface, you can use the Labelfor method directly to reset the displayed content using provider, and when we have multiple navigation properties of the same type and need to differentiate them when the page is displayed, You can use our new extension method (in fact, the display of any of the properties could use this extension method, not just this example.) such as lableforpropery (P = p.username)).
This way, the self-access of custom resources is improved.
In fact, we notice that this extension method is actually called html.lable (string expression, String labelText) in the Razor view. But the difference is that we change the hard-coded labeltext resource key value to dynamically generate the resource key value, this writing method is worth advocating. Imagine if one day, the resource key value scheme changes, for example, all key values are preceded by a application name as a prefix to distinguish between different applications, then hard coding, we need to make a lot of changes. Instead of extending the method, we just need to modify the extension method, append the prefix can be solved at once, and not for it?
. NET MVC4 Training Record VII (follow-up of autonomous access to resources)