How to keep control-specific css with the control code (ASP.NET) - c#

If I'm writing an ASCX control, and that control has markup that requires CSS, and that CSS will only be used by the control itself, is there an elegant way (besides just sticking it in the ASCX file itself) to include the .css file with it?
Ideally, I'd have control.ascx, control.ascx.cs, control.js, and control.css all as a little "package".
It brings up one problem and one concern so far:
Problem: Since the control is in a subdirectory, I don't want to use a tag hardcoded with the knoweldge that the css is in a subfolder. I'd like to write it so that it's relative to the control code (ie: same folder) but still be found at runtime.
Concern: If I did this for ten controls, it's ten more server hits I suppose. Maybe ScriptManager or the RadScriptManager or RadStyleSheet manager will magically aggregate them, but its not a showstopper for me either way.
Any ideas on how to solve the relative-path issue?

To get the correct path of your stylesheet relative to the page, call the ResolveClientUrl method of your user control, passing it the path of the stylesheet relative to the control:
HtmlLink link = new HtmlLink();
link.Href = this.ResolveClientUrl("control.css"); // same directory as the control
link.Attributes["type"] = "text/css";
link.Attributes["rel"] = "stylesheet";
this.Page.Header.Controls.Add(link);
Although each stylesheet will result in a separate request, you can mitigate the issue by enabling content expiration for them.

Related

Proper way of accessing resource file for a User Control added to DNN Module?

I'm working with the Christoc Module Template, trying to create a module with localized text. I've added a new User Control called TeamList.ascx to the project, and to the App_LocalResources folder I've added two new files:
TeamList.ascx.resx and TeamList.ascx.fr-CA.resx (for Canadian french).
I can include the user control in my view with no issues, but when the module loads on the page, all of the places containing localized text are blank.
I have tried registering the user control by including:
<moduleControl>
<controlKey>Teams</controlKey>
<controlSrc>
DesktopModules/LTSC_DashboardModule/TeamList.ascx
</controlSrc>
<supportsPartialRendering>False</supportsPartialRendering>
<controlTitle>Team Control</controlTitle>
<controlType>View</controlType>
<iconFile />
<helpUrl />
<viewOrder>0</viewOrder>
<supportsPopUps>True</supportsPopUps>
</moduleControl>
...in the DNN manifest file, but it has no effect.
I'm not sure what I'm missing here. I think it's probably something simple.
Thanks in advance for any help.
Accessing the Resource File is done by using the localization methods in DNN. You'll need to post how you are referencing the resource files to ultimately figure out what you are doing wrong.
Adding a resource file is simple, create a RESX file to match your ASCX file
TeamList.ascx would get a resource file in
App_LocalResources/ called TeamList.ascx.resx resulting in the path
/app_localresources/teamlist.ascx.resx
To access that path from code behind, you would simply call
var stringValue = Localization.GetString("STRINGNAME.Text", LocalResourceFile)
Where LocalResourceFile is inherited from PortalModuleBase (you need to have your ASCX file inherit the base class that my templates create, they inherit from PMB, or you can inherit from PMB directly.
Accessing it from within the ASCX file can be done similarly
<%=Localization.GetString("STRINGNAME.Text", LocalResourceFile)%>
Of from within controls with
<asp:label id="SOMEID" ResourceKey="STRINGNAME.Text" runat="server" />
The only other potential catch is if you are loading your ASCX file into another ASCX file, if so, you need to pass the moduleconfiguration to the child ASCX file in codebehind
I do this in the DNNSimpleArticle module with
var mbl = (dnnsimplearticleModuleBase)LoadControl(controlToLoad);
mbl.ModuleConfiguration = ModuleConfiguration;
mbl.ID = System.IO.Path.GetFileNameWithoutExtension(controlToLoad);
phViewControl.Controls.Add(mbl);
Does that get you pointed in the right direction?

Adding ASP.NET User Controls at runtime via <script runat="server">

I am having trouble executing a control inside the <script runat="server"> tags in an *.aspx page.
The control works when it is defined declaratively in the HTML section of the page, but I am unable to make it work when placed within the script tag itself.
At the beginning of the page I register my control with:
<%# Register assembly="App_Web_exemple.ascx.cc671b29" namespace="Moncontrol" tagprefix="moncontrol" %>
Then, in the HTML, I call it (successfully) with the following declaration:
<moncontrol:exemple ISBN="9782894646151" runat="server" />
When I try to add it programmatically within the <script runat="server">, however, I am unable to execute it. I tried with the tags <asp:Text /> and <asp:Literal />, as follows, but that also doesn't doesn’t work.
In the HTML part:
<asp:Text id="TestControl" runat="server" />
In the script part
TestControl.Text = "<moncontrol:exemple ISBN=\"9782894646151\" runat=\"server\" />";
To clarify, what you're looking to do is programmatically add a User Control to your Web Forms page at runtime. There are a few ways of accomplishing this.
Before we begin, it's worth noting that the code you wrote likely "works" insomuch that it compiles and doesn't throw a runtime error. But it's also not executing the control. I suspect if you look at your HTML, you'll find the control declaration being output as a string literal (i.e., unprocessed by the server). It is then disregarded by the browser since it doesn't know what the <moncontrol:exemple /> tag represents. That's obviously not what you want.
Establishing a Control Container
Regardless of which approach you take, you'll want to start with some type of container on your page that you can add the control to, such as a Panel. If you don't want the container to output any wrapper markup, you can use a Placeholder:
<asp:Placeholder id="ControlContainer" runat="server" />
This serves a similar purpose as your current Text control, except its only purpose is to provide a container that you will add your user control to. From ASP.NET's perspective, however, this can be any type of server control, including a <script runat="server">, as per your request. More on that later.
Programmatically Creating the Control
Next, you're going to create the control programmatically. This is where we run into various options. The most universal approach is to use ParseControl() method (reference). This looks something like this:
Control control = Page.ParseControl("<%# Register assembly=\"App_Web_exemple.ascx.cc671b29\" namespace=\"Moncontrol\" tagprefix=\"moncontrol\" %><moncontrol:exemple ISBN=\"9782894646151\" runat=\"server\" />");
That will parse the control using the same method that processes the declarative syntax on the page, and return a Control object with your Exemple control as the first control in its Controls collection.
I find that syntax a bit sloppy, however, since it's representing a .NET object and its properties as a string literal. Given that, there are some cleaner approaches. In this case, it appears that your control is being compiled into an assembly and, therefore, likely has a Code Behind defined which inherits from UserControl. If so, you should be able to simply do something like:
Exemple control = new Exemple();
And then set the properties on it programmatically, the way you would in any other C# object. Much cleaner!
If your control was instead being compiled dynamically by the server, then you'd instead use the Reference directive with the LoadControl() method, as described in the MSDN article How to: Create Instances of ASP.NET User Controls Programmatically. I don't believe that method will work for you, however.
Adding the Control Instance to the Page
Regardless of which approach you take, the next step is the same: you then add the control you've programmatically added to your page by adding it to the Controls collection of the target container. E.g.,:
ControlContainer.Controls.Add(control);
Note: You can technically just add this to the Page class's Control collection, too, but that doesn't give you any control over where on the page it is placed; having a PlaceHolder control (or equivalent) lets you specify exactly where you want the control to appear.
I hope this helps. There are a couple of caveats depending on how you wrote and compiled your control, but this should give you the basic structure needed to address your problem.

Disable Multiline Textbox Resize Grip on Server Side

I am dynamically creating controls, one of which is a multiline textbox. I have seen links about how to do it when it is hard coded into my aspx page, however I can't do that since the control is dynamically generated. I would like to avoid having to modify my .css files if possible. If anyone has any ideas on how to do this, any advice would be great thanks!
In general "resize:none" (there are funs of "overflow:auto") is the solution, therefore the typical option is to include it in your .css textarea definition and use the relative class name (if needed) in your aspx code. If needed means that by adding "textarea { resize: none; ... }" to your .css files there is no need to use CssClass in your asp:TextBox.
If you insist to not change your .css files, then you have to add a new css rule by JavaScript code somewhere (i.e. header control) on the server or add a new global .css file and serve it within your html header from aspx (this is better because you avoid checking existing css rules).

Convert User Controls to Server Controls

I'm wondering if anyone has any experience converting User controls to Web controls?
Ideally, I'd like to offload some of the design work to others, who would give me nicely laid out User Controls. Then, I could go through the process of converting, compiling, testing and deploying.
Until MS comes up with the magic "Convert to Server Control" option, it looks like I'm pretty well stuck with re-writing from scratch. Any ideas?
Is there a reason you must convert these user controls to server controls? Remember that it is possible to compile a user control into an assembly.
You are right there is no magic bullet here but since you already have a User Control its not that difficult.
Make sure all properties, events, etc. are set in the code behind since you won't have any mark up when you're done
Create a new Server Control
Paste all of the event handling and property setting code into the new control
override the Render method for each child control call the RenderControl Method passing in the supplied HtmlTextWriter
protected override void Render(HtmlTextWriter writer)
{
TextBox box = new TextBox();
//Set all the properties here
box.RenderControl(writer);
base.Render(writer);
}
I searched for hours and found many blogs about it.
The only thing worked for me was this article https://blogs.msdn.microsoft.com/davidebb/2005/10/31/turning-an-ascx-user-control-into-a-redistributable-custom-control/.
It says self-contained with given restrictions, but it does not mentions that the codebehind must be included in ascx file.
I used a Web Site project (not Web application!) and had to inline the code behind into the ascx file and only use control directive like:
<%# Control Language="C#" ClassName="MyPackage.MyControl"%>
So basically i just have a single file left for the user control. When codebehind was a separate file all control's where null when i referenced the final dll.
I also tried http://blog.janjonas.net/2012-04-06/asp_net-howto-user-control-library-compile-dll-file but with reflection the ascx file could not be found.

C#: Best way to inject CSS into MSHTML instance?

I'm trying to inject some CSS that accompanies some other HTML into a C# managed WebBrowser control. I am trying to do this via the underlying MSHTML (DomDocument property) control, as this code is serving as a prototype of sorts for a full IE8 BHO.
The problem is, while I can inject HTML (via mydomdocument.body.insertAdjacentHTML) and Javascript (via mydomdocument.parentWindow.execScript), it is flat-out rejecting my CSS code.
If I compare the string containing the HTML I want to insert with the destination page source after injection, the MSHTML's source will literally contain everything except for the <style> element and its underlying source.
The CSS passes W3C validation for CSS 2.1. It doesn't do anything too tricky, with the exception that some background-image properties have the image directly embedded into the CSS (e.g. background-image: url("data:image/png;base64 ...), and commenting out those lines doesn't change the result.
More strangely (and I am not sure if this is relevant), was that I was having no problems with this last week. I came back to it this week and, after switching around some of the code that handles the to-be-injected HTML before actual injection, it no longer worked. Naturally I thought that one of my changes might somehow be the problem, but after commenting all that logic out and feeding it a straight string the HTML is still appearing unformatted.
At the moment I'm injecting into the <body> tag, though I've attempted to inject into <head> and that's met with similar results.
Thanks in advance for your help!
tom
Ended up solving this myself:
mshtml.HTMLDocument test = (mshtml.HTMLDocument)webBrowser1.Document.DomDocument;
//inject CSS
if (test.styleSheets.length < 31) { // createStyleSheet throws "Invalid Argument if >31 stylesheets on page
mshtml.IHTMLStyleSheet css = (mshtml.IHTMLStyleSheet)test.createStyleSheet("", 0);
css.cssText = myDataClass.returnInjectionCSS(); // String containing CSS to inject into the page
// CSS should now affect page
} else {
System.Console.WriteLine("Could not inject CSS due to styleSheets.length > 31");
return;
}
What I didn't realize is that createStyleSheet creates a pointer that is still 'live' in the document's DOM... therefore you don't need to append your created stylesheet back to its parent. I ended up figuring this out by studying dynamic CSS code for Javascript as the implementations are pretty much identical.

Categories

Resources