Calling methods in App_Code from an ASP.NET Custom Control - asp.net

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
}
}
}

Related

The resource name is not a valid identifier for devexpress gridview

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();
}

ASP.NET MEF Compose all class parts

// In Global asax
DirectoryCatalog catalog = new DirectoryCatalog(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
CompositionContainer container = new CompositionContainer(catalog);
// do not want to compose at every instance when use Import attribute.
container.ComposeParts(this);
// add all external library (.dll)
container.ComposeParts(container);
// Index.aspx page
public partial Index: Page{
[Import]
private c1 object1;
protected void OnLoadPage
{
if (object1== null)
thrown Error("mef failed, why")
}
}
// External library Library1
[Export(typeOf(c1))]
public class c1{}
// External library Library2
[Export(typeOf(c2))]
public class c2{}
why object1 is null?
How to compose all classes from Global asax or simple to make some asp.net bootstrap lazy loading?
I do not want in every constructor to compose part. How to make all assembly or classes to be compose at the beginning in Global asax? All my [Export] are in external library classes. Main site project have reference to all .dll projects.
I am not sure the my question is correct, sorry for that.
Project is simple asp.net (no MVC) with external library with user controls.

Accessing BasePage from Master Page

I'm adding a custom control dynamically in master page code-behind:
try
{
// Add custom sidenav menu control dynamically
SideNavMenu sidenav = new SideNavMenu();
tempPath = Request.RawUrl.ToLower();
path = tempPath.Contains(#"/sitename") ? tempPath.Substring(7) : tempPath;
sidenav.MenuPath = path;
menuPlaceHolder.Controls.Add(sidenav);
}
catch
{
// Handle this - custom error form and email
// Master page needs access to base page LogError method
}
This is the base page with ErrorLog() method, which actually generates an email:
public partial class BasePage : System.Web.UI.Page
{
public void LogError(Exception error)
{
...
smtpClient.Send(message);
}
}
How can I call ErrorLog from master page code-behind? Or is there a better place to put the "common" ErrorLog method? Can someone suggest correct syntax or a better approach?
Place the LogError function in a class in your App_Code folder and probably make it a static function. If it's reusable through multiple projects, then place it in a seperate class library. Your BasePage class has no business implementing the details of logging errors and sending emails. Remember Separation of Concerns (SoC).
In fact, a library already exists to do this for you, called Elmah.
I highly recommend NLog for your purposes. It allows for configurable logging, including via SMTP. Your code would look like:
using Nlog;
public partial class BasePage : System.Web.UI.Page
{
private static Logger bpLogger = LogManager.GetCurrentClassLogger();
public void LogError(Exception error)
{
bpLogger.LogException(
LogLevel.Error,
"ruh roh",
error
);
...
//smtpClient.Send(message);
}
}
Because NLog is pretty efficient, you can instantiate a private static Logger object per page you are logging on. Additionally, the logger will automatically record which class it came from, and various configuration options allow you to change where it logs, how it logs and when it logs without changing any other code.
Here is a link to the configuration for sending errors via SMTP, for example:
https://github.com/nlog/NLog/wiki/Mail-target
It's a mature project that has been around for a very long time and can grow with your project as your logging needs grow.

access a public function in .ascx

how can i access a public function in an .ascx file using C#?
Thanks
If the function is not static you will need to first obtain an instance of the class containing the function and then invoke the function on this instance. For example:
<%
// obtain an instance of the type containing the function
Foo instance = new Foo();
// invoke the function on this instance
string result = instance.Bar();
%>
Obviously it would be better to do this in the code behind file instead of polluting your markup.
Like other public functions in .NET Framework - via object reference. But sometimes Visual Studio doesn't automatically see your User Control's public members. Try to rebuild your user control and the site if IntelliSense window doesn't show it to you.
Where are you calling the function from? The containing page? Masterpage? Parent control? The control itself? Regardless, you'll need to somehow obtain a reference to the control instance (unless the method is static) in order to invoke this method. And the type of your reference must match that of the class that defines the method.
Edit:
MyControl myControl = (MyControl)Page.FindControl("Id_Of_The_Control");
if (myControl != null)
{
myControl.TheMethod();
}
if you don't want to add your ascx control into a placeholder programmatically, just implement IAttributeAccessor and IUserControlDesignerAccessor interfaces to your user control class like;
public partial class yourascxclassname: System.Web.UI.UserControl, IAttributeAccessor, IUserControlDesignerAccessor
you can access only public members of your ascx control.

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();
}
}

Resources