The resource name is not a valid identifier for devexpress gridview - asp.net

I want to translate DevExpress GridView filter text from English to Persian.
I added a .resx file into asp.net project and fill it.
But I have some problem.
The resource name ASPxGridViewStringId.GroupPanel is not a valid identifier.
After added this .resx file, and run project, not happen any thing.
How can I fix this problem?

OK!!!! I do it, according to the similar question in this page, I added two ".resx" into App_GlobalResources folder.
one of them is default lang "English" and other is target lang "Persian".
DevExpress_Web_ASPxGridView_v15_1.rexs = default
DevExpress_Web_ASPxGridView_v15_1.Fa.resx = target
and translate it's values to persian.
finally change "UICULTURE" to "fa-ir".
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" UICulture="fa-ir" %>
Thanks God :) .
i put this answer in Devexpress

You can take a look at DevExpress Documentation topic called «Localization»:
DevExpress ASP.NET controls can be localized using one of the
following methods.
Via Satellite Resource Assemblies.This is the most universal approach, commonly used for all DevExpress products and under all
supported technologies.
Localizing ASP.NET Controls via Localizer Objects.This approach is appropriate if you are developing an application for a single culture
and you wish to translate resources into a specific language, or you
wish to change the default resources (for the English-US culture) to
their equivalents.
Satellite Resource Assemblies
You can create your translation via Localization Service:
After completing the translation you can save it, download it as the assembly and add it into your project:
To add localized resources to your application, copy satellite
assemblies from the unpacked archive to the corresponding subfolders
of your application's directory. The subfolder's name is the culture's
abbreviation. For example, to include German assemblies, copy the
folder named de from the \Bin\Framework\ directory to the Bin
directory of your application.
Localizer Objects
You can create custom localizer object for your translation and use it in your application. For GridView your object must be derived from ASPxGridViewLocalizer class or from ASPxGridViewResLocalizer class. In your descendant class you must ovveride XtraLocalizer(T).GetLocalizedString method. This method is using ASPxGridViewStringId enumeration as parameter.
Here is example:
public class CustomGridViewLocalizer : ASPxGridViewResLocalizer
{
public static void Activate()
{
var localizer = new CustomGridViewLocalizer();
var provider = new DefaultActiveLocalizerProvider<ASPxGridViewStringId>(localizer);
SetActiveLocalizerProvider(provider);
}
public override string GetLocalizedString(ASPxGridViewStringId id)
{
switch (id)
{
case ASPxGridViewStringId.AutoFilterBeginsWith:
return "شروع با";
case ASPxGridViewStringId.AutoFilterContains:
return "شامل";
case ASPxGridViewStringId.AutoFilterDoesNotContain:
return "شامل نمی شود";
case ASPxGridViewStringId.AutoFilterEndsWith:
return "به پایان می رسد با";
case ASPxGridViewStringId.AutoFilterEquals:
return "برابر";
case ASPxGridViewStringId.AutoFilterNotEqual:
return "نا برابر";
default:
return base.GetLocalizedString(id);
}
}
}
You need to activate this object in the Application_Start event handler within the Global.asax file:
protected void Application_Start(object sender, EventArgs e)
{
CustomGridViewLocalizer.Activate();
}

Related

Using a Razor #functions in several webpages (.cshtml files)

I have the below function that I would like to be available to several .cshtml views in my asp.net web pages 2 application. How can I make this function available to any view in the application (as opposed to just one).
#functions {
public bool DisplayButton(String startDate, String endDate)
{
return Convert.ToDateTime(startDate) < DateTime.Now && Convert.ToDateTime(endDate) > DateTime.Now;
}
}
Create a file called Functions.cshtml in App_Code and then paste the code you have into the file. Then you can call the DisplayButton method in any .cshtml file by prefixing it with the file name:
var myBool = Functions.DisplayButton(DateTime.Now, DateTime.Now.AddDays(30));
For more on working with functions and helpers in ASP.NET Web Pages, read this: http://www.mikesdotnetting.com/Article/173/The-Difference-Between-#Helpers-and-#Functions-In-WebMatrix
You can define "global" helper functions in a Razor file in the AppCode directory as described here: http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx. However, helpers only render page elements; they cannot return a value (or more correctly, the returned value is the HTML markup to be rendered).
If you need to return a value, your best bet is an extension method.
Don't see why you couldn't have a static class with a static method and just include it at the top of every view and then use it

Calling methods in App_Code from an ASP.NET Custom Control

I am using VS2008 and have a solution file which contains 1 Website and 1 Class Library Project.
The Class Library is a Custom Control which derives from Label. The Website contains a reference to the control - it builds successfully and the compiled .dll gets added to the Website's /bin folder. I can then use it in any of the website's .aspx pages without error.
What I cannot do, however, is reference any of the Website's data access methods that are in static classes in /App_Code from within the custom control.
I don't want to repeat the website data access logic all over again within the custom control when I know it will already exist in the website - I just want to be able to call a method from a class in /App_Code . If I try and reference anything in /App_Code from within the Class Library, it fails to build and says it can't find the Namespace or that it doesn't exist in the current context.
How can I achieve this so that the Custom Control builds as a standalone control, but can make use of classes in the website it gets used in? Delegates, possibly? Was hoping it might be more straightforward than that.
EDIT: I should add that the reason the control is in a separate Class Library is so that I can include JavaScript as an embedded resource within the Control. So when it's used in a .aspx page, it adds a WebResource.axd? style link to the page instead of a load of plaintext JavaScript in the <head> section.
EDIT 2:
In the website App_Code folder, I have a static class that handles data access (snippet):
[DataObject]
public static class DBAccess
{
[DataObjectMethod(DataObjectMethodType.Select)]
public static DataTable GetSomeData(Int32 SomeParam, DateTime OtherParam)
{
SqlConnection cn = SqlLibrary.GetConnection(DBConnectionString);
DataTable _dt;
SqlLibrary.SProcFill(out _dt, cn, "usp_SomeData_Select", SomeParam, OtherParam);
return _dt;
}
}
In the Class Library's custom control (which I want to build independently of the website's existence, yet be capable of calling its methods when used as a control in an .aspx page):
namespace MyCustomControls
{
public class StatusControl : Label
{
private Int32 _someProperty = -1;
private DateTime _otherProperty = DateTime.Now;
public StatusControl()
{
//some constructor logic
}
public void FetchData()
{
//what I'd **like** to do here is:
DBAccess.GetSomeData(_someProperty, _otherProperty);
//...but DBAccess isn't "visible" to this control at build time
}
}
}

Override a resource from standard assembly in ASP.NET

I want to override a string from a System.ComponentModel.DataAnnotations for an ASP.NET project. Do I need to make a satellite assembly, messing with custom build tasks, al.exe etc.? Even if yes, I couldn't find how to convert .resx to .resources to feed it to al.exe. And if no, where to put the .resx. and how to name it?
UPD: To make it clear: I wanted to use a custom resource string instead of one from the default resource from the assembly. I didn't want to make changes in the every place that uses that string. After all, the resources exist just for overriding them.
Phil Haack has an excellent article Localizing ASP.Net MVC Validation which specifically guides you through overriding your strings. This article applies more to DataAnnotations than it does ASP.net MVC. So, this will help however your are using DataAnnotattions.
Below I have listed the simplest steps to add Localized Resources in Visual Studio.
Open the Project Properties dialog.
Select the Resources tab.
Click to create a new default
resources file.
This will create two files in your Properties folder.
Resources.resx
Resources.Designer.cs
When Resources.resx has
opened, change it's Access Modifier
to Public.
Add your strings.
To add additional resource files for specific cultures you will need to.
Right click your Project in the
Solution Explorer.
Select Add -> New Item -> Resource
File.
Name it Resources.en-us.resx.
(replace 'en-us' with appropriate
code)
Click Add
Drag it into the Properties folder.
Open Resources.en-us.resx and change it's Access Modifier
to Public.
Add your strings.
Repeat for each Culture you need to
support.
During the build VS will convert the .resx files to .resource files and build wrapper classes for you. You can then access via the namespace YourAssembly.Properties.Resources.
With this using statement.
using YourAssembly.Properties;
You can decorate with attributes like this:
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = "MyStringName")]
Note: I used the Properties folder for consistency. To use the App_GlobalResources move your .resx files there and change your using statement to match the directory name. Like this:
using YourAssembly.App_GlobalResources;
Edit: The closest that you can get to Strongly Typed resource names would be to do something like this:
public class ResourceNames
{
public const string EmailRequired = "EmailRequired";
}
You can then decorate with attributes like this.
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = ResourceNames.EmailRequired)]
To enable automatic client culture detection add the globalizationsection to the web.config file.
<configuration>
<system.web>
<globalization enableClientBasedCulture="true" culture="auto:en-us" uiCulture="auto:en-us"/>
</system.web>
<configuration>
Here I have enabled a client based culture and set the culture and the uiculture to "auto" with a default of "en-us".
Creating Separate Satellite Assemblies:
The MSDN Creating Satellite Assemblies article will help as well.
If you are new to satellite assemblies make sure you read Packaging and Deploying Resources.
When creating satellite assemblies in the past, I have found it useful to use VS build events. These are the steps I would take.
Create a separate Class Library project in my solution.
Create or Add my .resx files to this project.
Add a Post-Build Event to the Project Properties dialog. (Like the one below)
Sample VS Post-Build Script:
set RESGEN="C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\resgen.exe"
set LINKER="C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\al.exe"
set ASSEMBLY=$(TargetName)
set SOURCEDIR=$(ProjectDir)
Set OUTDIR=$(TargetDir)
REM Build Default Culture Resources (en)
%RESGEN% %SOURCEDIR%en\%ASSEMBLY%.en.resx %SOURCEDIR%en\%ASSEMBLY%.resources
REM Embed Default Culture
%LINKER% /t:lib /embed:%SOURCEDIR%en\%ASSEMBLY%.resources /culture:en /out:%OUTDIR%%ASSEMBLY%.resources.dll
REM Embed English Culture
IF NOT EXIST %OUTDIR%en\ MKDIR $%OUTDIR%en\
%LINKER% /t:lib /embed:%SOURCEDIR%en\%ASSEMBLY%.resources /culture:en /out:%OUTDIR%en\%ASSEMBLY%.resources.dll
REM These are just a byproduct of using the project build event to run the resource build script
IF EXIST %OUTDIR%%ASSEMBLY%.dll DEL %OUTDIR%%ASSEMBLY%.dll
IF EXIST %OUTDIR%%ASSEMBLY%.pdb DEL %OUTDIR%%ASSEMBLY%.pdb
If you would prefer not to use ResGen.exe to convert your .resx files, you could do something like this.
using System;
using System.Collections;
using System.IO;
using System.Resources;
namespace ResXConverter
{
public class ResxToResource
{
public void Convert(string resxPath, string resourcePath)
{
using (ResXResourceReader resxReader = new ResXResourceReader(resxPath))
using (IResourceWriter resWriter = new ResourceWriter(
new FileStream(resourcePath, FileMode.Create, FileAccess.Write)))
{
foreach (DictionaryEntry entry in resxReader)
{
resWriter.AddResource(entry.Key.ToString(), entry.Value);
}
resWriter.Generate();
resWriter.Close();
}
}
}
}
One of the potential draw backs to doing the conversion this way is the need to reference the System.Windows.Forms.dll. You will still need to use Assembly Linker.
Edit: As wRAR has reminded us if you are signing your assemblies your keys must match.
While this is strange, especially for people familiar with open source localization technologies, one cannot build a satellite assembly for any system assembly or even a 3rd-party signed one:
If your main assembly uses strong
naming, satellite assemblies must be
signed with the same private key as
the main assembly. If the
public/private key pair does not match
between the main and satellite
assemblies, your resources will not be
loaded.
Whether the same is possible automatically, but without a satellite assembly, is unknown, though I doubt that.
If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in DataAnnotations validation messages. This epic hack works for us.
Go to "Microsoft .NET Framework 4.6.1 Language Pack" download page https://www.microsoft.com/en-us/download/details.aspx?id=49977
Select language and download
Extract NDP461-KB3102436-x86-x64-AllOS-{LANG}.exe with 7-Zip
Extract CAB file x64-Windows10.0-KB3102502-x64.cab with 7-Zip
Locate "msil_system.componentmod..notations.resources_...."
... in which you'll find "system.componentmodel.dataannotations.resources.dll"
Open .resources.dll with ILSpy, locate Resources and click Save button above String Table to save as System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources.{LANGUAGE}.resources
Add to your project under say a "Resources"
Ensure the files Build Action property of the resources files is set to "Embedded Resource"
Then in a PreStart method of your project you overwrite the System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources.resourceMan private static field (told you it was a hack) with the ones you have in your project.
using System;
using System.Linq;
using System.Reflection;
using System.Resources;
[assembly: WebActivator.PreApplicationStartMethod(typeof(ResourceManagerUtil), nameof(ResourceManagerUtil.PreStart))]
class ResourceManagerUtil
{
public static void PreStart()
{
initDataAnnotationsResourceManager();
}
/// <summary>
/// If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in
/// DataAnnotations validation messages. Here we override DataAnnotationsResources to use a ResourceManager that uses language .resources
/// files embedded in this assembly.
/// </summary>
static void initDataAnnotationsResourceManager()
{
var embeddedResourceNamespace = "<YourProjectDefaultNamespace>.<FolderYouSavedResourcesFilesIn>";
var dataAnnotationsResourcesName = "System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources";
var thisAssembly = typeof(ResourceManagerUtil).Assembly;
var dataAnnotationsAssembly = typeof(System.ComponentModel.DataAnnotations.ValidationAttribute).Assembly;
var resourceManager = new ResourceManager(embeddedResourceNamespace + "." + dataAnnotationsResourcesName, thisAssembly);
// Set internal field `DataAnnotationsResources.resourceMan`
var dataAnnotationsResourcesType = dataAnnotationsAssembly.GetType(dataAnnotationsResourcesName);
var resmanProp = dataAnnotationsResourcesType.GetField("resourceMan", BindingFlags.NonPublic | BindingFlags.Static);
resmanProp.SetValue(null, resourceManager);
}
}
Assuming that you want to override the default error message strings in the validation attributes, you can do that by setting the ErrorMessageResourceName and the ErrorMessageResourceType properties like this:
[Required(ErrorMessageResourceName = "Required_Username", ErrorMessageResourceType = typeof(MyResourceFile)]
public string Username { get; set; }
You can create a resource file called MyResourceFile.resx that contains Required_Username with the error message you want.
Hope this helps.
I want to provide an answer with the same idea as by Duncan Smart, but for .NET Core 2.2 instead of .NET Framework 4.x.
Here it is.
using System;
using System.Linq;
using System.Reflection;
using System.Resources;
public static class ResourceManagerHack
{
/// <summary>
/// If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in
/// DataAnnotations validation messages. Here we override DataAnnotationsResources to use a ResourceManager that uses language .resources
/// files embedded in this assembly.
/// </summary>
public static void OverrideComponentModelAnnotationsResourceManager()
{
EnsureAssemblyIsLoaded();
FieldInfo resourceManagerFieldInfo = GetResourceManagerFieldInfo();
ResourceManager resourceManager = GetNewResourceManager();
resourceManagerFieldInfo.SetValue(null, resourceManager);
}
private static FieldInfo GetResourceManagerFieldInfo()
{
var srAssembly = AppDomain.CurrentDomain
.GetAssemblies()
.First(assembly => assembly.FullName.StartsWith("System.ComponentModel.Annotations,"));
var srType = srAssembly.GetType("System.SR");
return srType.GetField("s_resourceManager", BindingFlags.Static | BindingFlags.NonPublic);
}
internal static ResourceManager GetNewResourceManager()
{
return new ResourceManager($"{typeof(<YourResource>).Namespace}.Strings", typeof(<YourResource>).Assembly);
}
private static void EnsureAssemblyIsLoaded()
{
var _ = typeof(System.ComponentModel.DataAnnotations.RequiredAttribute);
}
}
And I call this like so:
public static void Main(string[] args)
{
ResourceManagerHack.OverrideComponentModelAnnotationsResourceManager();
CreateWebHostBuilder(args).Build().Run();
}
Furthermore, I created a ~/Resources/<YourResource>.resx file and populated it with the default values and changed them at will. Lastly I created a public empty class <YourResource>.

Create Instance Aspx Page of Ascx Control In a Back End Class without Loading FilePath

Question: Is it possible in back end code (not in the code behind but in an actual back end class) to load and render a page or control defined in a .aspx or .ascx without having to use Load(path) and instead just create an instance of the page/control class?
I want to be able to do this (from a back end class NOT a code behind):
MyControl myCtl = new MyApp.Views.Shared.MyControl();
String html = Util.ControlToString(myCtl); //I get an empty string & hidden errors
instead of this
public static string ControlToString(string path)
{
Page pageHolder = new Page();
MyControl myCtl = (MyControl)pageHolder.LoadControl(path);
pageHolder.Controls.Add(myCtl);
StringWriter output = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, output, false);
return output.ToString();
}
Details:
In a Asp.net WebApp I occasionally need to render a user control (.ascx) or page (.aspx) as a HTML string. When a page or control inherits from a code behind, its class shows up in intellisense in my back end code and I can create an instance and set properties without getting compile time or run time errors. However, when I try to render the page or control I always get an empty string and upon inspection the page or control shows suppressed internal rendering errors unless I load the page or control using its physical file path.
I think the key issue has to do with when & how the .aspx / .ascx files are runtime compiled. I don't want to create a pre compiled class library of user controls because that would make the design process awkward and I really like the designer features offered by the .aspx / .ascx pages and so I'd love to find a way to make the pages compile in the solution so that they are usable like any other back end class but can still be created using the designer. I want the best of both worlds (1) to be able to edit pages and controls in the designer and (2) create instances and set their properties using back end classes.
Here is an approach that may help in situations like this.
The "back-end" code may not know where the user control is located, but the User Control does know where it is.
So, in the User Control, add a static method like this:
public partial class MyControl : UserControl
{
...
public static MyControl LoadControl(CustomDto initialData)
{
var myControl =
(MyControl)
((Page) HttpContext.Current.Handler)
.LoadControl("~\\UserControlsSecretPath\\MyControl.ascx");
myControl._initialData = initialData;
return myControl;
}
...
private CustomDto _initialData;
}
(The CustomDto is included to illustrate how initial data can be passed to the User Control. If you don't need to do that, take it out!)
With this, the code that loads the user control does not need to know the path to where the user control is physically located. If that location ever changes, then update this one location. All other code that uses this UserControl is unchanged.
In your back-end code, or anywhere else, you can do something this:
var control = MyControl.LoadControl(customDto);
PlaceHolder1.Controls.Add(control);
Generally speaking: no.
As far as I know, ASP.NET inherits from your classes to combine the .aspx/.ascx template with your code. This is why your controls show up empty: the code to combine the template with your code is missing. This is usually done by ASP.NET the first time you access a page or user control (that's precisely why the first hit is a little slow: it's actually generating and compiling the hookup-code).
For precompiled websites ASP.NET generates this code as part of your precompiled website's .dll in advance, which is why such sites load quicker. However, IIRC you'll still need to instantiate the generated classes rather than your original classes.
It's a pretty common request, but so far MS has not provided the tools to do this.
Edit: Although I fail to see why you'd want to render a control to an in-memory string, I might have a solution to the build problems.
If you stick to non-compiled .ascx files (using the web site model rather than the web application model), you can actually develop them separately by placing them physically in subfolder of your main project, and treat them as content files only. Then, you can make a separate project with this subfolder as the root folder. You only need to treat the files in this subfolder as web site files, the main project can still be a web application. (Actually recommended, 'cause you don't want the .csproj files included in the main project.)
However, shared code (that is, shared between the controls project and the main project) should be put in a separate library project, so you can compile each project separately without interdependencies.
Using LoadControl within the main project will compile them on the fly (code behind is possible); if you need to set properties, you must however define interfaces in the shared project, implement them on the appropriate user controls and cast the control created by LoadControl to the appropriate interface.
I developed a solution that solves my problem in VS 2008:
Create Main Site Solution: Create a MVC 1 Website solution in
VS 2008
Create Model Class Library: Add a Class Library for the Model Code
Create View Code: Add an "Empty Website" to hold the .ascx pages, and add a reference the model library
Create Deployment Site: Add a deployment project that compiles the "Empty Website" goto the "properties page" and Check: "Merge All outputs into a single assembly" and "Treat as library component" and be sure to UnCheck: "Allow this precompiled site to be updatable"
Reference Deployment Output: In the main project add a reference to the output of the Deployment site.
ASP. - Compiled Controls: Controls show up under the ASP. namespace and are named in two ways
A. if the .ascx / aspx page did not declare a "ClassName" then they are named using their folder and file name with underscores ex. <%# Control Language="C#" ClassName="Admin_Index" %>
B. if they did declare a class name then that is their name
List item
Usage: Example code is below
Here is an example usage
public ActionResult Index()
{
var ctl = new ASP.Directory_FirmProfile(); //create an instance
ctl.Setup(new MyDataModel); //assign data
//string test = CompiledControl.Render(ctl); //output to string
return new HtmlCtl.StrongView(ctl); //output to response
}
public class CompiledControl
{
public static string Render(Control c)
{
Page pageHolder = new Page();
pageHolder.Controls.Add(c);
StringWriter output = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, output, false);
return output.ToString();
}
public static void Render(Control c, StringWriter output)
{
Page pageHolder = new Page();
pageHolder.Controls.Add(c);
HttpContext.Current.Server.Execute(pageHolder, output, false);
}
public static void Render(Control c, HttpResponseBase r)
{
Page pageHolder = new Page();
pageHolder.Controls.Add(c);
HttpContext.Current.Server.Execute(pageHolder, r.Output, false);
}
}
public class StrongView : ActionResult
{
private Control ctl;
public StrongView(Control ctl)
{
this.ctl = ctl;
}
public string VirtualPath{get;set;}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HtmlCtl.CompiledControl.Render(ctl, context.HttpContext.Response);
}
}
I've come up with a simpler solution along the lines of Ruben's advice.
It has worked without problems for about a month:
//Example usage
//reference the control
var emailCTL = new HtmlCtl.ControlOnDisk<MyControlType>(#"~\Views\EmailTemplates\MyControlType.ascx");
//if you have a code behind you will get intellisense allowing you to set these properties
// and re-factoring support works most places except the template file.
emailCTL.c.title = "Hello World "; //title is a property in the code behind
emailCTL.c.data = data; //data is a property in the code behind
string emailBody = emailCTL.RenderStateless();
//Helper Class
public class ControlOnDisk<ControlType> where ControlType : UserControl
{
public ControlType c;
Page pageHolder = new Page();
public ControlOnDisk(string path)
{
var o = pageHolder.LoadControl(path);
c = (ControlType)o;
pageHolder.Controls.Add(c);
}
public string RenderStateless()
{
StringWriter output = new StringWriter();
// set up dumby context for use in rendering to email stream
StringBuilder emailMessage = new StringBuilder();
TextWriter tw = new StringWriter(emailMessage);
HttpResponse dumbyResponse = new HttpResponse(tw);
HttpRequest dumbyRequest = new HttpRequest("", "http://InsertURL.com/", ""); //dummy url requierd for context but not used
HttpContext dumbyContext = new HttpContext(dumbyRequest, dumbyResponse);
//HttpContextBase dumbyContextBase = new HttpContextWrapper2(dumbyContext);
dumbyContext.Server.Execute(pageHolder, output, false);
return output.ToString();
}
}

How to use System.Web.Routing to not URL rewrite in Web Forms?

I am using System.Web.Routing with ASP.NET (3.5) Web Forms that will URL rewrite the following URL from
http://www.myurl.com/campaign/abc
to
http://www.myurl.com/default.aspx?campaign=abc
The code is as below:
public static void RegisterRoutes(RouteCollection routes)
{
routes.Add("CampaignRoute", new Route
(
"{campaign_code}",
new CustomRouteHandler("~/default.aspx")
));
}
IRouteHandler implementation:
public class CustomRouteHandler : IRouteHandler
{
public CustomRouteHandler(string virtualPath)
{
VirtualPath = virtualPath;
}
public string VirtualPath { get; private set; }
public IHttpHandler GetHttpHandler(RequestContext
requestContext)
{
if (requestContext.RouteData.Values.ContainsKey("campaign_code"))
{
var code = requestContext.RouteData.Values["campaign_code"].ToString();
HttpContext.Current.RewritePath(
string.Concat(
VirtualPath,
"?campaign=" + code));
}
var page = BuildManager.CreateInstanceFromVirtualPath
(VirtualPath, typeof(Page)) as IHttpHandler;
return page;
}
However I noticed there are too many things to change on my existing aspx pages (i.e. links to javascript, links to css files).
So I am thinking if there's a way to keep above code but in the end rather than a rewrite just do a Request.Redirect or Server.Transfer to minimize the changes needed. So the purpose of using System.Web.Routing becomes solely for URL friendly on the first entry.
How to ignore the rest of the patterns other than specificed in the code?
Thanks.
Using rewriting combined with ASP.NET URL Routing is not recommended because some implementations of ASP.NET URL Routing internally use rewriting as well (it depends on the version of ASP.NET). The combination of two different components using rewriting can cause conflicts (though I'm not 100% sure that that's why you're seeing this problem).
Regarding using transfer/redirect/rewrite:
My strongest recommendation would be to not use any of them! Instead of redirecting (or anything else) just let the page be called directly by ASP.NET by returning it from the IRouteHandler, much as you are already doing (just without the call to Rewrite). As long as your IRouteHandler saves the RouteData somewhere, the Page can then get the data from the route and you should be good to go.
Take a look at Phil Haack's Web Form routing sample to see an example of how to save the route data (or just use his code!).
Regarding ignoring patterns:
You can use an IRouteConstraint to constrain which URLs match your route. There is a built-in default route constraint implementation that uses regular expressions, but you can also write custom route constraints. Here is an example:
Route r = new Route(...);
r.Constraints = new RouteValueDictionary(new {
campaign_code = "\d{5}", // constrain to 5-digit numbers only
other_value = new CustomRouteConstraint(), // call custom constraint
});
CustomRouteConstraint is a class that you can write that derives from IRouteConstraint.
One thing I should note about static files such as CSS and JPG files is that by default they are always excluded from routing. By default routing ignores patterns that exactly match physical files on disk. You can change this behavior by setting RouteTable.Routes.RouteExistingFiles = true, but that is not the default.

Resources