Exploring the depths and potentials of ASP.NET RSS 2.0 or Subscribe to .BenRush by Email
 Monday, March 26, 2007

I was helping someone out on the newsgroups who wanted to draw a GIF from his ASP.Net web page; one of the requirements was that it had a transparent background. As it turns out, this little exercise is a bit more complicated than it at first seems due to - what I think - is either a bug or an anomaly in the GDI+ framework.

First - a bit of education. GIF is an indexed image format, meaning that it uses a color palette and individual pixels reference a color by indexing into the palette. One of the "colors" in the palette is a non-color or transparent; a transparent color is anything with an alpha value of 0 (ie both 0-255-255-255 and 0-128-128-128 are "invisible" where the first digit represents the value of the alpha octet).

Interestingly, the .Net runtime's GDI+ Graphics.FromImage() method DOES NOT like indexed file formats, so if you want to draw onto a GIF using the GDI+ framework, it cannot be done via this method. It's screwy; don't ask me why it is this way. Well, this guy wanted to draw text onto a GIF with a transparent background, so....how to you go about drawing text onto a GIF with a transparent background?

My first thought was modifying the palette so that all references to, say, black had an alpha transparency level of 0 - meaning invisible. What this would mean is that anything that referenced a black index into the color palette wouldn't be visible (100% alpha value). Another interesting "quirk" of the .Net runtime GDI+ framework, however, is that whenever you save the data to a stream (file, memory stream, output stream, etc) it will screw with the color palette. I didn't really try to understand what it was doing, but if you look at the color palette in a debugger window while using it, the save method just toys with it and mucks up your mods to it.

So...the only option, lock down the data, and reference each individual pixel in the image, pointing it to the entry in the GIFs color palette for a transparent color instead of black. And it worked....here is the code (implemented as a C# library that you may use freely - please be mindful that I have not tested this but for just a little bit, there may be resource leaks, etc):

public class TransparentGif
{
 public TransparentGif()
 {
 }
    public static MemoryStream DrawTransparentGif(
        String text, Color textColor, Int32 width, Int32 height)
    {
        Bitmap drawableBMP = new Bitmap(width, height);
        Graphics gdc = Graphics.FromImage(drawableBMP);
        try
        {
            gdc.FillRectangle(new SolidBrush(Color.Black), new Rectangle(0, 0, width, height));
            gdc.DrawString(text, new Font("Arial", 13), new SolidBrush(textColor), 0, 0);
            MemoryStream memStream = new MemoryStream();
            drawableBMP.Save(memStream, ImageFormat.Gif);

            memStream.Seek(0, SeekOrigin.Begin);
            System.Drawing.Image gifed = System.Drawing.Image.FromStream(memStream);

            ColorPalette cp = gifed.Palette;
            Int32 alpha = 0;
            for (Int32 c = 0; c < cp.Entries.Length; c++)
            {
                if (cp.Entries[c].A == 0)
                {
                    alpha = c;
                     break;
                }
            }

            BitmapData data = ((Bitmap)gifed).LockBits(
    new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, gifed.PixelFormat);

            Byte[] buffer = new Byte[width* height];
            Marshal.Copy(data.Scan0, buffer, 0, width* height);

            for (Int32 c = 0; c < buffer.Length; c++)
            {
                if (cp.Entries[buffer[c]].ToArgb() == Color.Black.ToArgb())
                {
                    buffer[c] = (Byte)alpha;
                }
            }

            Marshal.Copy(buffer, 0, data.Scan0, buffer.Length);

            ((Bitmap)gifed).UnlockBits(data);

            MemoryStream outStream = new MemoryStream();
            gifed.Save(outStream, ImageFormat.Gif);
            outStream.Seek(0, SeekOrigin.Begin);

            return outStream;
        }
        finally
        {
            if (gdc != null)
                gdc.Dispose();
        }
    }
}

If you comment out the actual pixel manipulation, what gets drawn to my web page is the following:

...or a GIF with a black background. If you bring back in the actual pixel manipulation part of the above code, you get this:

Total alpha transparency of the GIF....

Addendum:

For an example of using this class, see here.

Recommended reading:

kick it on DotNetKicks.com
Monday, March 26, 2007 12:19:27 PM (Central Standard Time, UTC-06:00)  #    Comments [1] - Trackback
ASP.Net | Programming | WinForms and WPF
 Sunday, March 25, 2007

The earlier you can do it in the page lifecycle, the better. Make sure you do it before the page's prerender event.

    void b_Click(object sender, EventArgs e)
    {
        ((Label)FindControl("mylabel")).Text = DateTime.Now.ToString();
    }
    protected void Page_Load(object sender, EventArgs e)
    {

        ScriptManager sm = new ScriptManager();
        sm.ID = "sm1";
        sm.SupportsPartialRendering = true;
        sm.EnablePartialRendering = true;

        this.form1.Controls.Add(sm);

        UpdatePanel up = new UpdatePanel();
        up.ID = "up";
        up.RenderMode = UpdatePanelRenderMode.Block;
        up.UpdateMode = UpdatePanelUpdateMode.Always;

        this.form1.Controls.Add(up);

        Button b = new Button();
        b.Text = "Click me";
        b.Click += new EventHandler(b_Click);

        up.ContentTemplateContainer.Controls.Add(b);

        Label l = new Label();
        l.Text = "";
        l.ID = "mylabel";

        up.ContentTemplateContainer.Controls.Add(l);
    }


kick it on DotNetKicks.com
Sunday, March 25, 2007 12:35:27 AM (Central Standard Time, UTC-06:00)  #    Comments [0] - Trackback
ASP.Net | AJAX
 Saturday, March 24, 2007

If you happen to have an ASP.Net blog, etc. and want to add links to many of the popular traffic sites out there; you can use this code:

<a href="http://del.icio.us/post?url=<%=this.Request.Url.ToString() %>&title=<%=this.Page.Title %>">Del.icio.us</a>
<a href="http://digg.com/submit?phase=2&url=<%=this.Request.Url.ToString() %>">Digg</a>
<a href="http://technorati.com/cosmos/search.html?url=<%=this.Request.Url.ToString() %>">Technorati</a>
<a href="http://blinklist.com/index.php?Action=Blink/addblink.php&url=<%=this.Request.Url.ToString() %>&Title=<%=this.Page.Title %>" >Blinklist</a>
<a href="http://furl.net/storeIt.jsp?t=<%=this.Page.Title %>&u=<%=this.Request.Url.ToString() %>">Furl</a>
<a href="http://reddit.com/submit?url=<%=this.Request.Url.ToString() %>&title=<%=this.Page.Title %>" >reddit</a>


kick it on DotNetKicks.com
Saturday, March 24, 2007 12:22:14 PM (Central Standard Time, UTC-06:00)  #    Comments [0] - Trackback
ASP.Net | Programming

The following C# code will programmatically scroll the browser window. The steps are easy.

First, add your web browser control to the form.

Second, add an event handler for the document completed event:

        private void Form1_Load(object sender, EventArgs e)
        {
            this.webBrowser1.Url = new Uri("
http://news.google.com");
            HtmlDocument doc = this.webBrowser1.Document;
            this.webBrowser1.DocumentCompleted +=
                new WebBrowserDocumentCompletedEventHandler(
                  
webBrowser1_DocumentCompleted);
        }

Third, fill in the event handler:

        void webBrowser1_DocumentCompleted(object sender, 
               
WebBrowserDocumentCompletedEventArgs e)
        {
            HtmlDocument doc = this.webBrowser1.Document;
            doc.Body.ScrollTop = 300;
            return;
        }

You can then programmatically set the body's scrolltop property to adjust the position of the body element within the scrollable area. For example, this is the page without the event handler code added:

And now with the scrolling code added:

 

Recommended reading:

kick it on DotNetKicks.com
Saturday, March 24, 2007 1:02:08 AM (Central Standard Time, UTC-06:00)  #    Comments [0] - Trackback
Programming | WinForms and WPF
 Thursday, March 22, 2007

A nice facility of ASP.Net AJAX is that it lets you write a lot of functionality into the client whereas before you were stuck to simply writing it all on the server. One perfect example of this is authentication/authorization, which is now accessible from client script code through the AuthenticationService class.

I will lay out the basics of writing your own Authentication service (overriding the default one of ASP.net AJAX, which uses SQL Server, by the way). Many people will more than likely end up using their own implementation at some point anyway, not everyone has the benefit of creating a system from the ground-up.

First, enable the Authentication in your ASP.Net AJAX application by adding this to your web.config file:

<system.web.extensions>
  <scripting>
    <webServices>
      <authenticationService enabled="true" />
    </webServices>
  </scripting>
</system.web.extensions>

Second, enable FormsAuthentication in your web application (if it's not already), by adding this to your web.config file:

<system.web>
  <authentication mode="Forms">
    <forms cookieless="UseCookies"
      loginUrl="~/login.aspx"/>
  </authentication>
<system.web>

Third, you must specify a web service which the client code can asynchronously interact with to verify your login/logout actions. You do this by right-mouse clicking on the ScriptManager for your particular page and setting the AuthenticationService property to the URI of the service:

Fourth, you write your web service. Your web service must contractually have two methods with the following signatures:

    [WebMethod]
    public bool Login(string userName,
        string password, bool createPersistentCookie)
    {
        //Place code here.
        return true;
    }

    [WebMethod]
    public void Logout()
    {
        //Place code here.
    }

NOTE: You MUST decorate your WebService class with the [ScriptService] attribute (<ScriptService()> in VB.Net) or else you will get back a funky error from the script runtime. The error I got was "The server method 'Login' failed". I also got the error code 12031 when this occured.

Fifth, you add your script code to the aspx file to handle the authentication calls. The script code interactions with the AuthenticationService class in script and registers for callbacks that notify you about login/logout status.

       <script language="javascript" type="text/javascript">
        function Button1_onclick() {
            Sys.Services.AuthenticationService.set_defaultLoginCompletedCallback(
                OnLoginCompleted);
            Sys.Services.AuthenticationService.set_defaultFailedCallback(
                OnFailed);
            Sys.Services.AuthenticationService.set_defaultLogoutCompletedCallback(
                OnLogoutCompleted);
               
            Sys.Services.AuthenticationService.login("test",
                "test", false,null,null,null,null,"User Context");
            return;
        }
        function OnLoginCompleted(validCredentials,
            userContext, methodName)
        {
            alert("You're logged in");
            return;
        }
        function OnFailed(error,
            userContext, methodName)
        {
            alert("Failed to log in " + error.get_message() + ". " + error.get_statusCode());
            return;
        }
        function OnLogoutCompleted(result)
        {
            alert("You've logged out");
            return;
        }
        </script>

You then run the code and watch as the script will call your service method, and return back to the client framework whatever result it got from the service. You will need to flesh out the methods in your service to actually leverage the FormsAuthentication framework, etc. A typical situation would be like this:

[WebMethod]
public bool Login(string userName, string password, bool createPersistentCookie)
{
    if (Membership.Provider.ValidateUser(userName, password))
    {
        FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
        return true;
    }
    return false;
}
[WebMethod]
public void Logout()
{
    FormsAuthentication.SignOut();
}


kick it on DotNetKicks.com
Thursday, March 22, 2007 3:22:33 PM (Central Standard Time, UTC-06:00)  #    Comments [0] - Trackback
AJAX | ASP.Net | Programming

RegisterDataItem is a subtle yet powerful member of the ScriptManager class. Unknown to many developers of ASP.Net AJAX is the fact that there can be a back-end data stream flowing from the server to the controls on the client whenever a partial rendering event takes place anywhere on the page. Through RegisterDataItem, you get to register a particular block of data (raw data or JSON serialized data) to a particular control on the client; and client code you place on the page can very easily update that control with its registered data. Example...

Say you have a calendar control on your web form inside an UpdatePanel, and outside the UpdatePanel there exists a Label control whose text should display the selected date of the calendar:

This design is for purposes of this example only; you probably wouldn't always set your system up this way, but for now it suffices.

The question is, how would you go about updating the Label as a result of a refresh of UpdatePanel1 in the example above? The answer is the RegisterDataItem.

In the postback method for clicking a particular date in the calendar, do this:

protected void Calendar1_SelectionChanged(object sender, EventArgs e)
{
    this.ScriptManager1.RegisterDataItem(this.Label_SelectedDate, this.Calendar1.SelectedDate.ToString());
}
...when this method is called asynchronously, it will register the string data representing our date with Label_SelectedDate. We write a very simple block of client code immediately after our ScriptManager tag in the page:

<script>
Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(PageLoadingHandler);

function PageLoadingHandler(sender,args){
   var dataItems = args.get_dataItems();
   if($get('Label_SelectedDate')!==null)
   $get(
'Label_Selecteddate').innerHTML = dataItems['Label_SelectedDate'];
   return;
}
</script>

The result will be, whenever you select a date within the calendar, the AJAX runtime will properly update the contents of the label:

This works because the ASP.net AJAX runtime sends the serialized data down to the client framework during the partial rendering of the calendar; the client framework will snag the appropriate bits from this feed and update the controls accordingly through the script we wrote. The "trick" is registering a method with the pageLoading event of the PageRequestManager.


kick it on DotNetKicks.com
Thursday, March 22, 2007 12:40:14 PM (Central Standard Time, UTC-06:00)  #    Comments [0] - Trackback
AJAX | ASP.Net | Programming
 Tuesday, March 20, 2007

One caveat to using the ScriptManager on your pages is that you can only have one ScriptManager reference at a time. What if you are writing a content page for a master page that has a ScriptManager on it; but you need to now make sure that the ScriptManager references a particular service your content page needs - what do you do? You could check out the MaterPage and make the necessary modifications - but why? That's actually not a very good idea because it kind of breaks the whole concept of master/content page seperation; especially in scenarios where a certain developer can't or shouldn't have access to the master page (say you're farming a particular content page out to a consultant, etc).

The answer when you need to reference a service from your content page and yet the ScriptManager resides on the MasterPage is a ScriptManagerProxy. The ScriptManagerProxy works by detecting the main (real) ScriptManager on your page at runtime and hooking itself to that ScriptManager, making sure that any references given to it are also given to the real ScriptManager. In fact, it's constructor takes a real ScriptManager as a parameter:

internal ScriptManagerProxy(IScriptManagerInternal scriptManager)
{
    this._scriptManager = scriptManager;
}
...and it's OnInit method (called by the ASP.net page framework when the page is being built) is where the proxy registers itself with the page's real ScriptManager:

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);
    if (!base.DesignMode)
    {
        this.ScriptManager.RegisterProxy(this);
    }
}
As a result of this registration, when the real ScriptManager builds all references for Services during OnPagePrePrenderComplete, its RegisterServices() method will collect all service references from registered ScriptManagerProxies....

    if (this._proxies != null)
    {
        foreach (ScriptManagerProxy proxy in this._proxies)
        {
            proxy.RegisterServices(this);
        }
    }


kick it on DotNetKicks.com
Tuesday, March 20, 2007 10:31:11 AM (Central Standard Time, UTC-06:00)  #    Comments [0] - Trackback
AJAX | ASP.Net | Programming
 Monday, March 19, 2007

ASP.Net AJAX introduces the ScriptManager, and the ScriptManager introduces a set of API that look just like a set exposed by the ClientScriptManager in "traditional" ASP.Net; things like RegisterStartupScript, RegisterClientScriptInclude, etc.

The question is: why?

The answer is actually pretty elementary, but in really understanding it you kind of come to another understanding of ASP.net AJAX; well, actually, AJAX in general.

The way the ClientScriptManager (Me.Page.ClientScript or this.Page.ClientScript) works in "traditional" ASP.Net it maintains a list of all scripts on the Page, and when asked to render by the Page object, it properly works those scripts into the page output. The thing is, in ASP.Net AJAX, the page lifecycle is hijacked by the ScriptManager during partial rendering and therefore the Page is never asked to render; the ScriptManager takes care of choosing what controls to render, etc.

So code like this: 



  
protected void Button1_Click (object sender, EventArgs e){
    
this.LinkButton1.Visible=true;
    Page.ClientScript.RegisterStartupScript(
this.GetType(), "linkonclick""<script>document.getElementById('"+this.LinkButton1.ClientID+"')"+".onclick = function(){('click',alert('hello world'),true);}</script>");
  }
}

.....will NOT work if Button1 is inside of an UpdatePanel - the ClientScriptManager is never asked to render its scripts; you're basically adding scripts to it for no good reason. This code DOES work, however, if Button1 is not within an UpdatePanel.

To make the above code work in an UpdatePanel you do the following....

  protected void Button1_Click (object sender, EventArgs e){
    
this.LinkButton1.Visible=true;
    ScriptManager.RegisterStartupScript(
thisthis.GetType(), "linkonclick""<script>document.getElementById('"+this.LinkButton1.ClientID+"')"+".onclick = function(){('click',alert('hello world'),true);}</script>"false);
  }

Nothing really changed except that we're now calling through the ScriptManager's static version of the RegisterStartupScript method. Since the script is registered with the ScriptManager, it is sent down to the client in the partial page refresh response as something the client script needs to introduce into the page DOM. Everything works now.
kick it on DotNetKicks.com
Monday, March 19, 2007 3:13:00 PM (Central Standard Time, UTC-06:00)  #    Comments [1] - Trackback
AJAX | ASP.Net | Programming

Computers Blogs - Blog Top Sites

Archive
<March 2007>
SunMonTueWedThuFriSat
25262728123
45678910
11121314151617
18192021222324
25262728293031
1234567
Blogroll
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008
Benjamin Rush
Sign In
Statistics
Total Posts: 444
This Year: 0
This Month: 0
This Week: 0
Comments: 127
Themes
Pick a theme:
All Content © 2008, Benjamin Rush
DasBlog theme 'Business' created by Christoph De Baene (delarou)