locked
Creating an Image adapter RRS feed

  • Question

  • User1984373067 posted

    Hi,

    I would like to create an adapter for the image web control to cure a pet peeve of mine. Currently, if I write out:

     <asp:Image ImageUrl="myPic.jpg" AlternateText="Some text" runat="server" />

     the html that is emitted is:

    <img src="myPic.jpg" alt="Some text" style="border-width:0px" />

    I would like that inline style struck out completely for every image on my site. As it turns out, in the System.Web.UI.WebControls.Image class resides the AddAttributesToRender method. At the very bottom in an IF statement, the following:

    else
    {
        writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "0px");
    }  

     I think creating a custom adapter would be the most elegant solution, but I'm unsure as to how to tackle this. It's my understanding that the adapter replaces the control's rendering logic, but at the end of the day I only want to edit an IF statement.  I don't want to have to re-invent the wheel to replace the System.Web.UI.WebControls.Image and System.Web.UI.WebControls.WebControl classes rendering logic. Is there a simple way to achieve this?

    Kind regards,

    Chris
     

    Thursday, December 28, 2006 11:52 AM

All replies

  • User1415983342 posted
    What do you mean under "custom adapter"?

    In order to elaborate solution, it is necessary to nderstand your problem.
    Why do you want to get rid of inline
    style="border-width:0px"
    insertion?
    Do you want to get rid just from this insertion or completely disable such possibility for developer?
    Friday, December 29, 2006 3:39 AM
  • User1415983342 posted
    You may use Html Server Control Image (from Html section of toolbar) instead of webcontrol Image (from Standard section of Toolbox).
    Then you wouldn''t have style attribute at all
    Friday, December 29, 2006 5:24 AM
  • User1984373067 posted

    Inline styles contravene the idea of separating style and markup. Presentational details should be defined in an external CSS, not peppered throughout your HTML, and by extension, I'd rather not have web controls emitting them. Inline styles have higher specificity than styles defined in an external or embedded stylesheet, so making global presentational changes is impossible without editing all your server controls.

    I am hoping someone can point me in the right direction of creating my own control adapter for the Image web control. These adapters are discussed here:

    http://msdn.microsoft.com/msdnmag/issues/06/10/ExtremeASPNET/

    If anyone is familiar with them, please let me know.

    Chris
     

    Friday, December 29, 2006 8:21 AM
  • User1415983342 posted
    I am familiar with control adapters.

    But I do not understand what you are trying to achieve, why and how it is related to control adapters
    If you do not like existing Image webcontrol, you can create your own control or even component from the scratch

    I suspect that your question would have been better suited for "Architecture" forum
    http://forums.asp.net/16/ShowForum.aspx
    Saturday, December 30, 2006 3:30 AM
  • User1415983342 posted
    This was initially posted in  "Custom Server Controls"
    Saturday, December 30, 2006 8:45 PM
  • User1984373067 posted

    I am working within a team that is comprised of members with varying degrees of knowledge of ASP.NET and CSS. I am creating an environment for us to create a rather large web app. What I would like to do is allow them to use the asp.net web controls like they are used to, but have those controls emit HTML that will require the least amount of maintenance. That means striking out inline styles from any page that is in a production environment.

    So, while I could extend the Image web control and create my own custom control, it would be one more thing for everyone to remember, and if someone forgets, it could end up impacting the rendering of a page. The most elegant solution would be to allow everyone to soldier on using "<asp:Image ..." but having an adapter step in and alter the rendering ever so slightly so that the inline style never gets emitted.

    The image web control is just one of the controls that I'd like to tinker with, I'd eventually like to remove the inline styles from any web control we use. If using adapters in this fashion is not possible, then I will have to create a raft of custom controls and hope everyone remembers to use them.

    Regards,

    Chris

    Sunday, December 31, 2006 12:43 PM
  • User1415983342 posted
     
    IMHO you would need  to define and support dozens of  control adapters per each control having a pain in the ass that I cannot really understand why

    There is nothing to remeber if you:
    1) call your custom control class Image and derive it from System.Web.UIControl class
    (so eliminating any style issues):
    namespace YourCustomServerControlsLibraryNamespace
    {
                public class Image : System.Web.UI.Control
                {
    2) 
    linking it to asp tagprefix in web.config
    <system.web>
        <
    pages
    >
            <
    controls
    >
                <
    add tagPrefix="asp" namespace="YourCustomServerControlsLibraryNamespace" assembly="YourCustomServerControlsLibraryAssembly"
    />
            </
    controls
    >
        </
    pages >

    Thereafter you can use it in exactly the same way as <asp:Image ... or just drag$dropping after adding reference or placing it into GAC
    Tuesday, January 2, 2007 6:43 AM
  • User-534056067 posted

    I don't understand what is meant by "dozes of control adapters per control." The control architecture allows at most one adeapter per control for the whole app. Adapters are not like user controls or custom controls.  You just write one for each type of control (class) that you want to change the rendering for.

    OK, let me get right to the stated problem: getting rid of the inline style for the img tag rendered by the native implementation of the Image class (for the asp:image tag).

    First, let's set up a test.  To do so, install the latest rev of the CSS Friendly adapters (from www.asp.net/cssadapters).  Then in VS create a new web site based on the ASP.NET CSS Friendly Web Site template (in the New Web Site dialog in VS).  Now, in the root of that new web site, create a test pages (test.aspx) like this:

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="test.aspx.cs" Inherits="test" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <asp:Image runat="server" ImageUrl="~/WalkThru/arrowRight.gif" />
        </div>
        </form>
    </body>
    </html>

    It's code behind (test.aspx.cs) will look like this:

    using System;
    using System.Data;
    using System.Configuration;
    using System.Collections;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;

    public partial class test : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }
    }

    This is pretty basic stuff.  I created this page using the "Add New Item" menu item in VS and chose to add a new web form with a code behind page. Essentially the only change I made to the page was to add the <asp:Image>.  If you now run this page and look at the HTML source it produces in a browser you'll find that the <img> does, indeed, have the offending inline style to set the border width to 0px.

    Now add the following code in a new file App_Code\Adapters\ImageAdapter.cs:

    using System;
    using System.IO;
    using System.Text.RegularExpressions;
    using System.Web;
    using System.Web.UI;

    namespace CSSFriendly
    {
        public class ImageAdapter : System.Web.UI.WebControls.Adapters.WebControlAdapter
        {
            protected override void RenderBeginTag(HtmlTextWriter writer)
            {
                StringWriter sw = new StringWriter();
                HtmlTextWriter hw = new HtmlTextWriter(sw);
                base.RenderBeginTag(hw);
                hw.Flush();
                hw.Close();
                string origTag = sw.ToString();
                string newTag = Regex.Replace(origTag, "\\s*style=[\"'][^\"']*[\"']", "", RegexOptions.Multiline | RegexOptions.IgnoreCase);
                writer.Write(newTag);
            }

            protected override void RenderEndTag(HtmlTextWriter writer)
            {
            }

            protected override void RenderContents(HtmlTextWriter writer)
            {
            }
        }
    }

    Basically, we are just stripping off the offensive inline style from the <img> tag.  We are, otherwise, simply using the base rendering to create the <img> tag... which what the author of this thread originally said they desired.

    After creating this new adapter in your App_Code\Adapters folder you need to add a reference to it in your .browser file in App_Browsers. Otherwise, your web app won't actually use this new adapter.  So, you need to modify App_Browsers\CSSFriendlyAdapters.browser.  Add the following to the first section in that file:

          <adapter controlType="System.Web.UI.WebControls.Image"
                   adapterType="CSSFriendly.ImageAdapter" />

    Now run your test page again and look at the source produced in the browser.  The inline style will be gone... just like you wanted.

    Let me know if you have problems with this.  Also, please note that though I wrote this in C# it is perfectly plausible to do the equivalent using VB.NET.

    Consider this a gift for the New Year.  Cheers.

    Tuesday, January 2, 2007 1:20 PM
  • User1984373067 posted

    That is *EXACTLY* what I want...

    Thank you very much, this is my birthday, Xmas and New Year's gift all rolled into one! As someone who is pretty stubborn when it comes to his markup and CSS, I really appreciate the effort in giving ASP.NET a little push to be more compliant...

    Again, many thanks!

    Chris 

    Wednesday, January 3, 2007 9:28 AM
  • User-288480790 posted

    Russ,

    Once again, a perfect example of (a) how silly it is the MS doesn't properly support CSS and, more importantly, (b) how freakin' cool this adapter "framework" is.

    A developer now has two solid tools in his box: writing a new control to replace the existing one if the changes are functional, or simply adapting the existing one if only the styling is at issue.

    Thursday, January 4, 2007 11:00 AM
  • User1067329908 posted
    Russ, you are a prince among men. This is marvellous. And seems to work just fine even without the CSSFriendly control adapters installed too (as long as you pick your own namespace).
    Friday, February 23, 2007 10:46 AM
  • User1415983342 posted

    I don't understand what is meant by "dozes of control adapters per control." The control architecture allows at most one adeapter per control for the whole app. 


    [1] tells
    "By default, the ASP.NET page framework maps a single instance of an adapter to each control for each request."
    "A common example of adaptive control behavior is adaptive rendering wherein an ASP.NET Web page is rendered specific to the browser or markup."
    Basically it is described as creating a control adapter for each browser/device per each control.

    Never-ending manual task?

    Well, I had neither wrote it myself nor invented/implemented as such. If it is wrong, I am interested to read comments on it.

    [1]
    Architectural Overview of Adaptive Control Behavior
    ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.en/dv_aspnetctrlauth/html/4ff05ae9-4109-4352-929e-ad893895dbff.htm
    Friday, February 23, 2007 11:28 PM
  • User1415983342 posted

    So, while I could extend the Image web control and create my own custom control, it would be one more thing for everyone to remember, and if someone forgets, it could end up impacting the rendering of a page.


    Well, I do not see where is the gain instead of using a custom server control go into installing add-in and using control adapters. Is it more simple to use, implement and to remeber just because you want the others would not see some excessive properties in Property Grid at design time????????

    Honestly...

    And if someone forgot about your custom control, what is the problem - seeing some excessive properties in Properties Gid?
    I am interested to understand

    Friday, February 23, 2007 11:43 PM
  • User1415983342 posted
     
    Honestly, I could not get the idea of necessity:
    -  to install CSS Control Adapter Toolkit, which is available for download at www.asp.net/cssadapters
    - to program ImageAdapter
    - to modify App_Browsers\CSSFriendlyAdapters.browser
    And you gonna to do this in each project?! For what?

    And this all would break with some unknown device, monitor resolution or device

    Just because you see something unused and even not available at design time.

    You can simply use it directly the custom server control and instll it in GAC. It will be automatically available to all projects!
    2    using System.IO;
    3    using System.Text.RegularExpressions;
    4    using System.Web.UI;
    5    using System.Web.UI.WebControls;
    6    
    7    namespace CustomEventNotFiring
    8    {
    10      [ToolboxData("&lt;{0}:ImgOverrideRender runat=server></{0}:ImgOverrideRender>")]
    11      public class ImgOverrideRender : Image//ImageButton?
    12      {
    13   
    14         public override void RenderBeginTag(HtmlTextWriter writer)
    15         {
    16            StringWriter sw = new StringWriter();
    17            HtmlTextWriter hw = new HtmlTextWriter(sw);
    18            base.RenderBeginTag(hw);
    19            hw.Flush();
    20            hw.Close();
    21            string origTag = sw.ToString();
    22            string newTag = Regex.Replace(origTag, "\\s*style=[\"'][^\"']*[\"']", "", RegexOptions.Multiline | RegexOptions.IgnoreCase);
    23            writer.Write(newTag);
    24         }
    25         public override void RenderEndTag(HtmlTextWriter writer)
    26         {
    27         }
    29      }
    30   }
        
    The same and hundreds of time more simple and versatile?
    Saturday, February 24, 2007 12:59 AM
  • User1415983342 posted
    public class Image : System.Web.UI.WebControls.Image//or you may use ImageButton?
    {

    instead of
    11      public class ImgOverrideRender : Image//ImageButton?
    12      {

    also works perfectly, if you need Image name
     
    Saturday, February 24, 2007 1:05 AM
  • User-534056067 posted

    Guenavan,

    The .browser file lets you associate a set of control adapters to a set of browsers. Therefore, you do not need to write a control adapter for each kind of browser. You can write a single control adapter that is appropirate for "modern" browsers (e.g., browsers that understand and can properly handle CSS 2.0+) but automatically have old browser requests handled by the native ASP.NET 2.0 framework. For example, this kit's Menu adapter can be set up in the .browser file so it is used for modern versions of Netscape Navigator but not for really, really old versions that are better served using the older table-based rendering offered by the native ASP.NET 2.0 framework.

    That is one advantage of using adapters.

    Another, and probably even more important, is the fact that adapters let you modify the rendering of legacy (existing) web sites without modifying their server-side markup. Imagine you have a legacy ASP.NET web site that contains hundreds of pages (ASPX files) with thousands of instances of <asp:Image>. Rather than replacing all of those <asp:Image> tags in all of those pages with some custom control like <acme:MyBetterImageControl> you can simple hook an image adapter into your site by adding a simple .browser file. That way, you can modify the rendering behavior of all of those <asp:Image> tag instances without actually changing their markup.

    If you use custom controls and you decide that you want to return to using the native control or want to use a new kind of custom control for that sort of situation instead then you have to modify your server-side markup.  When you use adapters you can simply delete the .browser file or modify it so it uses a different adapter. That is far less work, potentially.

    Now you may be thinking, "Russ seems to be really anti-custom-controls." That isn't so. I have written custom controls for ASP.NET for many years and will continue to do so. (For example, my friend John Albano and I wrote the custom controls used by Dreamweaver to support ASP.NET 1.0 and 1.1.  See, http://www.adobe.com/support/dreamweaver/building/aspnet_tags/. So I'm certainly familiar with custom controls and do advocate their use in some situations.)  I simply believe that there are plenty of cases where adapters provide a better solution than custom controls... and, therefore, authors should try to learn and understand how/when to use adapters in addition to using custom controls.

    Your point seems to be that you can achieve the same rendering with a custom control that you can achieve with an adapter. Yes, that is definitely true in many (or even all) cases... but that misses the larger point that adapters let you modify the rendering behavior of a site without touching a pages's markup. Plus using adapters is an easy way to effectively tell the framework to render one way for one set of browsers but render another way for a different (perhaps old, less capable) set of browsers.

    Earlier in this thread it seemed like you were saying that you hadn't yet tried using this kit or any adapters. To get a better understanding of why adapters (and this kit of them in particular) has value you might want to spend some time using them. I suspect you'll come to appreciate them more when you do. You might also want to read up on what others say about using adapters.  For example, http://msdn.microsoft.com/msdnmag/issues/06/10/ExtremeASPNET/.

    Saturday, February 24, 2007 12:25 PM
  • User-1548791341 posted
     
    Honestly, I could not get the idea of necessity:
    -  to install CSS Control Adapter Toolkit, which is available for download at www.asp.net/cssadapters
    - to program ImageAdapter
    - to modify App_Browsers\CSSFriendlyAdapters.browser
    And you gonna to do this in each project?! For what?

    And this all would break with some unknown device, monitor resolution or device

    Just because you see something unused and even not available at design time.

    You can simply use it directly the custom server control and instll it in GAC. It will be automatically available to all projects!
    2    using System.IO;
    3 using System.Text.RegularExpressions;
    4 using System.Web.UI;
    5 using System.Web.UI.WebControls;
    6
    7 namespace CustomEventNotFiring
    8 {
    10 [ToolboxData("&lt;{0}:ImgOverrideRender runat=server></{0}:ImgOverrideRender>")]
    11 public class ImgOverrideRender : Image//ImageButton?
    12 {
    13
    14 public override void RenderBeginTag(HtmlTextWriter writer)
    15 {
    16 StringWriter sw = new StringWriter();
    17 HtmlTextWriter hw = new HtmlTextWriter(sw);
    18 base.RenderBeginTag(hw);
    19 hw.Flush();
    20 hw.Close();
    21 string origTag = sw.ToString();
    22 string newTag = Regex.Replace(origTag, "\\s*style=[\"'][^\"']*[\"']", "", RegexOptions.Multiline | RegexOptions.IgnoreCase);
    23 writer.Write(newTag);
    24 }
    25 public override void RenderEndTag(HtmlTextWriter writer)
    26 {
    27 }
    29 }
    30 }
     
    The same and hundreds of time more simple and versatile?

    Guenavan, to support what Russ is saying here:

    I'm a student worker for Web Services at my university. We are using control adapters to modify GridView, DetailsView, FormView, DataList, RadioButtonList, and CheckBoxList currently.

    You do not have to go through

    -  to install CSS Control Adapter Toolkit, which is available for download at www.asp.net/cssadapters
    - to program ImageAdapter
    - to modify App_Browsers\CSSFriendlyAdapters.browser

     

    every time you want to use an adapter. We're using one DLL containing all of the adapters in the GAC. While you could modify the master browser file to have the adapters used in all sites automatically, we chose not to so that it's easier for developers to use the unadapted controls just in case they needed to, so we're still using a .browser file at each site.  So adapters can be automatically available to all projects just like custom controls.

     Custom controls are great, but I think this is where you're getting confused. The point of adapting the controls is that we do not want to change the functionality of the controls whatsoever. I like how CheckBoxList works, for example, but I don't like how it renders in a table. I want to retain all of the functionality of CheckBoxList, but separate out the presentation so that it is controlled by CSS instead of by the markup of the control itself.

     The problem with a lot of the default .NET controls is that they mix markup and presentation together. The HTML produced should not have presentational code mixed in and should allow you to make all the presentational changes you need with CSS. That is what the adapters are trying to solve, not a change in control functionality, and it's why we aren't worried about losing presentational properties in the controls.

     You also get the added benefit that you can change markup for different browsers, but you can also just tell the browser file to use the adapters for every browser. With custom controls, you don't get that flexibility.

     With the adapted controls, we have that separation of presentation from content. Now, if my university wants a site-wide redesign, all we have to do is change our CSS file and every single page is good to go. Hopefully that helps you understand why we're using adapters instead of custom controls. =)

    Monday, February 26, 2007 12:01 PM
  • User1415983342 posted
    Thanks a lot.
    Is it possible to apply control adapters to user controls (ascx) also?

    It seems there are problems with integrating them smoothly 
    http://forums.asp.net/1595807/ShowThread.aspx#1595807
    http://forums.asp.net/thread/1589153.aspx
    in some cases
    Monday, February 26, 2007 6:02 PM
  • User1371732994 posted

    Hey Falcon,

    I tried to use an adapter for a checkboxlist and a radiobuttonlist. Rendering is just fine, except when I use it e.g. in a wizard control, I make a selection, go to the next step, go back, the selection I made is gone. Disabling the adapter shows the selections I made just fine. So I guess I'm missing something. Could you point me in the right direction? Thanks!

     

    Grtz

    Sunday, April 15, 2007 9:43 AM
  • User-1548791341 posted

    I'm not sure what your RadioButtonList and CheckBoxList adapters look like.

    The last post of http://forums.asp.net/thread/1407982.aspx (by bdemarzo) is probably the best current RadioButtonList code out there at the moment, but since we aren't using the wizard control here at work until I get the chance to help adapt it this summer, my knowledge on that aspect is limited. I'd try the code in that post to see if that fixes your problem, then go from there.

     Cheers!


     

    Monday, April 16, 2007 12:19 PM
  • User1371732994 posted
    Thanks for pointing me to that thread. Missed that one. I'll go and find out what's happening :-)
    Monday, April 16, 2007 2:18 PM
  • User1893902719 posted

    Thanks, that's what I'm after too, I have Controls that inherit form the Image control and I don't want to have to change all the occurrences of that either.

    One suggestion I have is tweak that regex so if someone does add an inline style intentionally then it "should" work.

    My regex isn't uber enough for the task :-)

     

    Cheers. 

    Murray. 

    Monday, October 22, 2007 8:43 PM