Ahmad Masykur

Share your knowledge although one function!

About the author

Ahmad Masykur is a Software Architecture Engineer at PT. Freeport Indonesia Jakarta Indonesia.
In this blog, I share things of interest to me. Most topics are likely to be related to software development, but don't hold me to it.

Awards


Certificates



Powered by

Prayer Time

November 21, 2009
الفجر/Dawn 04:03:24
الشروق/Sunrise 05:26:10
الظهر/Noon 11:39:09
العصر/Afternoon 15:02:40
المغرب/Sunset 17:52:08
العشاء/Dusk 19:06:07
Jakarta  Indonesia
Prayer time widget created by
Ahmad Masykur

Page List

Validators


Ahmad Masykur

Create DeepZoom with/without Silverlight using Seadragon

Seadragon is Deep Zoom implementation purely using JavaScript without Silverlight. You can zoom in/out using mousewheel and drag it to pan arround as sample picture below.

To create seadragon like picture above, you can upload your image to http://seadragon.com. You will receive unique URL after uploading images is done which can be embedden to your web page. Seadragon will automatically checking your machine. If you have Silverlight installed on your machine, Silverlight version will be used. If Silverlight is not installed yet, Seadragon will use javascript version.


Permalink | Comments (0) | Post RSSRSS comment feed

Silverlight 3.0 Available for Download Today

Microsoft officials wouldn’t say a month ago whether the actual Silverlight 3 bits would be available by the day of the official launch, which is July 10. But it turns out they are available a day earlier.

If you try to install this version but you have already Silverlight 2.0 for Developer installed on your machine, you will get error message: Unable to install Silverlight. Your Silverlight developer components are out of date.

image

If you are not a developer and want to avoid this error, please uninstall previous Silverlight version first by following the Silverlight Uninstall Instructions. If you are a developer, download and install Silverlight 3.0 for Developer here.

image

Installation will upgrade current version of Silverlight on your machine. You do not need to install Silverlight 3.0 if you have installed Silverlight 3.0 for developer.

image

Same as previous version, Silverlight shipped not followed with developer tools. Expression Blend 3 and Silverlight Tools for Visual studio still Beta or RC. Expression Blend 3 + SketchFlow RC available for download from http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=92e1db7a-5d36-449b-8c6b-d25f078f3609. I'm still waiting for RTM version of developer tools.


Categories: Silverlight
Permalink | Comments (4) | Post RSSRSS comment feed

Create IE8 Accelerator for BlogEngine.NET 1.5

I have created IE8 WebSlices for BlogEngine.NET 1.5 yesterday. I will add new feature stuff to BlogEngine to support IE8 features. Today, I have wrote new IE8 accelerator for BlogEngine.NET 1.5. I’m added new handler named AcceleratorHandler to BlogEngine to handle accelerator request. This handler provide accelerator XML information and preview page.

I have add new menu named Accelerator in my BlogEngine theme.

image

Click Accelerator menu to add the accelerator and then click Add button on the confirmation dialog window.

image

If we navigate away from our site and want to search text from our site. Select text inside the page and navigate to the accelerator. Now you can search from your site anywhere. Here is screen capture of the accelerator.

image

To enable this feature to BlogEngine.NET. Please follow steps below.

Create AcceleratorHandler

Create new handler for accelerator in BlogEngine.Core project. Here is the source code of the accelerator.

using System;
using System.Collections.Generic;
using System.Web;
using System.Xml;
using System.IO;
using System.Web.UI;
namespace BlogEngine.Core.Web.HttpHandlers
{
    class AcceleratorHandler : IHttpHandler
    {
        #region IHttpHandler Members
        public bool IsReusable
        {
            get { return false; }
        }
        public void ProcessRequest(HttpContext context)
        {
            if (!string.IsNullOrEmpty(context.Request.QueryString["q"]))
            {
                // Searches posts and pages
                WriteSearchResult(context);
            }
            else
            {
                WriteAcceleratorInfo(context);
            }
        }
        private void WriteSearchResult(HttpContext context)
        {
            List<IPublishable> searchResult = Search.Hits(context.Request.QueryString["q"], false);
            context.Response.ContentType = "text/html";
            context.Response.AppendHeader("Content-Disposition", "inline; filename=search.html");
            Stream  stream = context.Response.OutputStream;
            using (TextWriter streamWriter = new StreamWriter(stream))
            {
                using (HtmlTextWriter htmlTextWriter = new HtmlTextWriter(streamWriter))
                {
                    htmlTextWriter.WriteFullBeginTag("html");
                    htmlTextWriter.WriteFullBeginTag("head");
                    htmlTextWriter.WriteFullBeginTag("title");
                    htmlTextWriter.Write(BlogSettings.Instance.Name);
                    htmlTextWriter.WriteEndTag("title");
                    htmlTextWriter.WriteEndTag("head");
                    htmlTextWriter.WriteBeginTag("body");
                    htmlTextWriter.WriteAttribute("style", "font-family:Arial,Hevetica,Sans-serif;");
                    htmlTextWriter.Write(HtmlTextWriter.TagRightChar);
                    htmlTextWriter.WriteFullBeginTag("dl");
                    foreach (IPublishable result in searchResult)
                    {
                        htmlTextWriter.WriteFullBeginTag("dt");
                        htmlTextWriter.WriteBeginTag("a");
                        htmlTextWriter.WriteAttribute("href", result.AbsoluteLink.ToString());
                        htmlTextWriter.Write(HtmlTextWriter.TagRightChar);
                        htmlTextWriter.Write(result.Title);
                        htmlTextWriter.WriteEndTag("a");
                        htmlTextWriter.WriteBeginTag("br");
                        htmlTextWriter.Write(HtmlTextWriter.SelfClosingTagEnd);
                        htmlTextWriter.WriteBeginTag("dd");
                        htmlTextWriter.WriteAttribute("style", "margin:0px;font-size:smaller;");
                        htmlTextWriter.Write(HtmlTextWriter.TagRightChar);
                        string description;
                        if (string.IsNullOrEmpty(result.Description))
                        {
                            string desc = Utils.StripHtml(result.Content);
                            string shortDesc;
                            if (desc.Length < 200)
                            {
                                shortDesc = desc;
                            }else
                            {
                                shortDesc = desc.Substring(0, 196) + " ...";
                            }
                            description = shortDesc;
                        } else
                        {
                            description = result.Description;
                        }
                        htmlTextWriter.Write(description);
                        htmlTextWriter.WriteEndTag("dd");
                        htmlTextWriter.WriteEndTag("dt");
                    }
                    htmlTextWriter.WriteEndTag("dl");
                    htmlTextWriter.WriteEndTag("body");
                    htmlTextWriter.WriteEndTag("html");
                }
            }
            context.Response.Flush();
            context.Response.End();
        }
        private void WriteAcceleratorInfo(HttpContext context)
        {
            context.Response.ContentType = "application/xml";
            context.Response.AppendHeader("Content-Disposition", "inline; filename=accelerator.xml");
            XmlWriterSettings writerSettings = new XmlWriterSettings();
            writerSettings.Encoding = System.Text.Encoding.UTF8;
            writerSettings.Indent = true;
            Stream stream = context.Response.OutputStream;
            using (XmlWriter writer = XmlWriter.Create(stream, writerSettings))
            {
                writer.WriteStartElement("openServiceDescription", "http://www.microsoft.com/schemas/openservicedescription/1.0");
                writer.WriteElementString("homepageUrl", Utils.AbsoluteWebRoot.ToString());
                writer.WriteStartElement("display");
                writer.WriteElementString("name", "Search in " + BlogSettings.Instance.Name + " weblog");
                writer.WriteElementString("icon", new Uri(Utils.AbsoluteWebRoot, "pics/blogengine.ico").ToString());
                writer.WriteEndElement();
                writer.WriteStartElement("activity");
                writer.WriteAttributeString("category", "search");
                writer.WriteStartElement("activityAction");
                writer.WriteAttributeString("context", "selection");
                writer.WriteStartElement("preview");
                writer.WriteAttributeString("action", new Uri(Utils.AbsoluteWebRoot, "accelerator.axd").ToString());
                writer.WriteStartElement("parameter");
                writer.WriteAttributeString("name", "q");
                writer.WriteAttributeString("value", "{selection}");
                writer.WriteEndElement();
                writer.WriteEndElement();
                writer.WriteStartElement("execute");
                writer.WriteAttributeString("action", new Uri(Utils.AbsoluteWebRoot, "search.aspx").ToString());
                writer.WriteStartElement("parameter");
                writer.WriteAttributeString("name", "q");
                writer.WriteAttributeString("value", "{selection}");
                writer.WriteAttributeString("type", "text");
                writer.WriteFullEndElement();
            }
            context.Response.Flush();
            context.Response.End();
        }
        #endregion
    }
}

 

Add handler to web.config file.

I the httlHandlers, add following line

<httpHandlers>
    <add verb="*"
         path="accelerator.axd"
         type="BlogEngine.Core.Web.HttpHandlers.AcceleratorHandler, BlogEngine.Core"
         validate="false"/>

In the handlers element, add following line

<handlers accessPolicy="Read, Write, Script, Execute">
    <add name="AcceleratorHandler" 
         verb="*" 
         path="accelerator.axd" 
         type="BlogEngine.Core.Web.HttpHandlers.AcceleratorHandler, BlogEngine.Core" 
         resourceType="Unspecified" 
         requireAccess="Script" 
         preCondition="integratedMode"/>

Add new button to your selected theme. Your modification may look like lines below.

<a href="javascript:window.external.AddService('<%=Utils.AbsoluteWebRoot + "accelerator.axd" %>');" style="float:left">Accelerator</a>

Rebuild you solution and republish to hosted server.

Enjoy with new stuff of IE8 accelerator.


Categories: BlogEngine
Permalink | Comments (0) | Post RSSRSS comment feed

Create WebSlices for BlogEngine.NET 1.5

Web Slices are snippets of the entire page that a user can subscribe to. Web Slices will be kept updated by the browser automatically, and can be viewed directly from the Favorites bar, complete with graphics and visuals. BlogEngine.NET is most popular blog software developed in ASP.NET technology. WebSlices is not yet supported by latest version of BlogEngine.NET (version 1.5.0). I will show you step by step creating WebSlices for BlogEngine.NET.

Prepare service for WebSlices

WebSlices can use XML RSS service to display the data. WebSlices will display first item of RSS information. To display n of latest blog posts, we should modify RSS service to show all information in latest item. BlogEngine use syndication handler for RSS which served by SyndicationHandler.cs file in BlogEngine.Core assembly. Please modify following files to extend syndication handler to be support WebSlices.

SyndicationFormat.cs

Add new enum named WebSlices

/****************************************************************************
Modification History:
*****************************************************************************
Date		Author		  Description
*****************************************************************************
04/11/2007  brian.kuhn    Created SyndicationFormat Enumeration
08/30/2007  brian.kuhn    Moved SyndicationFormat enum to root of library
06/13/2009  Ahmad Masykur Add WebSlices enumeration
****************************************************************************/
using System;
namespace BlogEngine.Core
{
    /// <summary>
    /// Represents common types of syndication formats.
    /// </summary>
    public enum SyndicationFormat
    {
        /// <summary>
        /// No syndication format specified.
        /// </summary>
        None    = 0,
        /// <summary>
        ///  Indicates that a feed conforms to the Atom syndication format.
        /// </summary>
        Atom    = 1,
        /// <summary>
        /// Indicates that a feed conforms to the RSS syndication format.
        /// </summary>
        Rss     = 2,
        /// <summary>
        /// Indicates that a feed conforms to the IE8 WebSlices syndication format.
        /// </summary>
        WebSlices = 3
    };
}

SyndicationHandler.cs

In SetHeaderInformation method, add highlighted lines below.

/// <summary>
/// Sets the response header information.
/// </summary>
/// <param name="context">An <see cref="HttpContext"/> object that provides references to the intrinsic server objects (for example, <b>Request</b>, <b>Response</b>, <b>Session</b>, and <b>Server</b>) used to service HTTP requests.</param>
/// <param name="items">The collection of <see cref="IPublishable"/> instances used when setting the response header details.</param>
/// <param name="format">The format of the syndication feed being generated.</param>
/// <exception cref="ArgumentNullException">The <paramref name="context"/> is a null reference (Nothing in Visual Basic) -or- the <paramref name="posts"/> is a null reference (Nothing in Visual Basic).</exception>
private static void SetHeaderInformation(HttpContext context, List<IPublishable> items, SyndicationFormat format)
{
	DateTime lastModified = DateTime.MinValue;
	foreach (IPublishable item in items)
	{
		if (item.DateModified.AddHours(-BlogSettings.Instance.Timezone) > lastModified)
			lastModified = item.DateModified.AddHours(-BlogSettings.Instance.Timezone);
	}
	switch (format)
	{
		case SyndicationFormat.Atom:
			context.Response.ContentType = "application/atom+xml";
			context.Response.AppendHeader("Content-Disposition", "inline; filename=atom.xml");
			break;
		case SyndicationFormat.Rss:
			context.Response.ContentType = "application/rss+xml";
			context.Response.AppendHeader("Content-Disposition", "inline; filename=rss.xml");
			break;
		case SyndicationFormat.WebSlices:
			context.Response.ContentType = "application/rss+xml";
			context.Response.AppendHeader("Content-Disposition", "inline; filename=webslices.xml");
		break;
	}
	if (Utils.SetConditionalGetHeaders(lastModified))
		context.Response.End();
}

SyndicationGenerator.cs

Add new WriteWebSlicesFeed method below

private void WriteWebSlicesFeed(Stream stream, List<IPublishable> publishables, string title)
{
    XmlWriterSettings writerSettings = new XmlWriterSettings();
    writerSettings.Encoding = System.Text.Encoding.UTF8;
    writerSettings.Indent = true;
    using (XmlWriter writer = XmlWriter.Create(stream, writerSettings))
    {
        writer.WriteStartElement("rss");
        writer.WriteAttributeString("version", "2.0");
        writer.WriteStartElement("channel");
        writer.WriteElementString("title", "Latest Posts");
        writer.WriteElementString("ttl", "15");
        writer.WriteStartElement("item");
        writer.WriteElementString("title", title);
        StringBuilder description = new StringBuilder();
        description.Append("<div style=\"font-family: arial, helvetica, sans-serif;\">");
        description.Append("<div style=\"background-color: #f0f0f0;\"><h1 style=\"margin:0px; padding:0px; font-size: x-large;\">" + title + "</h1><h2 style=\"margin:0px; padding:0px; font-size: small; font-weight: normal; font-style: italic;\">" + blogSettings.Description + "</h2></div>");
        description.Append("<ul style=\"list-style-type: none;margin: 0;padding: 0px;\">");
        for (int i = 0; (i < 10) && (i < BlogSettings.Instance.NumberOfRecentPosts); i++)
        {
            description.Append("<li style=\"margin-left: 0px;\"><div style=\"display:block; border-top: 1px solid #efefef; border-bottom: 1px solid #efefef;\"><a href=\"" + publishables[i].AbsoluteLink + "\" target=\"_blank\" title=\"" + publishables[i].Title + "\" style=\"text-decoration: none; font-size: small; color: #000090; width:500px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; \">" + publishables[i].Title + "</a></li>");
        }
        description.Append("</ul>");
        description.Append("</div>");
        writer.WriteElementString("description", System.Web.HttpUtility.HtmlDecode(description.ToString()));
        writer.WriteEndElement();
        writer.WriteFullEndElement();
    }
}

Add highlighted lens below in WriteFeed method.

/// <summary>
/// Writes a generated syndication feed that conforms to the supplied <see cref="SyndicationFormat"/> using the supplied <see cref="Stream"/> and collection.
/// </summary>
/// <param name="format">A <see cref="SyndicationFormat"/> enumeration value indicating the syndication format to generate.</param>
/// <param name="stream">The <see cref="Stream"/> to which you want to write the syndication feed.</param>
/// <param name="publishables">The collection of <see cref="IPublishable"/> objects used to generate the syndication feed content.</param>
/// <param name="title">The title of the RSS channel</param>
public void WriteFeed(SyndicationFormat format, Stream stream, List<IPublishable> publishables, string title)
{
	if (stream == null)
	{
		throw new ArgumentNullException("stream");
	}
	if (publishables == null)
	{
		throw new ArgumentNullException("publishables");
	}
	if (!stream.CanWrite)
	{
		throw new ArgumentException(String.Format(null, "Unable to generate {0} syndication feed. The provided stream does not support writing.", format), "stream");
	}
	//------------------------------------------------------------
	//	Write syndication feed based on specified format
	//------------------------------------------------------------
	switch (format)
	{
		case SyndicationFormat.Atom:
			this.WriteAtomFeed(stream, publishables, title);
			break;
		case SyndicationFormat.Rss:
			this.WriteRssFeed(stream, publishables, title);
			break;
		case SyndicationFormat.WebSlices:
			this.WriteWebSlicesFeed(stream, publishables, title);
			break;
	}
}

Master page file of selected theme.

Find <asp:contentplaceholder /> element and add hslice class to parent element of placeholder. Add some information to provide WebSlice title and service source. Your modification of master page may look like following lines.

<div id="main" class="hslice">
    <div style="display:none">
        <p class="entry-title">
            <%=BlogSettings.Instance.Name %>
        </p>
        <a rel="feedurl" href="<%=Utils.AbsoluteWebRoot + "syndication.axd?format=webslices" %>"></a>
    </div>
    <asp:contentplaceholder id="cphBody" runat="server">
    </asp:contentplaceholder>
</div>

Build BlogEngine solution. Your BlogEngine is now support WebSlices feed. You only redeploy BlogEngine.Core.dll and selected theme to make WebSlices support of your blog.

Happy nice blogging with IE8.


Tags: ,
Categories: BlogEngine
Permalink | Comments (0) | Post RSSRSS comment feed

BlogEngine.NET 1.5 was released a few days ago

BlogEngine.NET was released a few days ago as announced by Mads in his blog. In this release, blogengine.net now support some IIS7 features. New features in this release are:

  • Nested comments
  • Superb Windows Live Writer integration (including tags)
  • Latest TinyMCE text editor
  • Mono 2.4 support that just works
  • Doesn't screw with jQuery and Prototype anymore
  • Better database support out of the box
  • Higher performance
  • ...and of course a lot of general improvements, tweaks and bug fixes

This release is claimed more stable than older.

Al Nyveldt also posted installation screen cast in his blog.

You can learn by watching this video to install BlogEngine.NET with XML or database data.

This is release is probably be the last release supporting IIS6 and .NET 2.0. In the next release, BlogEngine will be only support II7 and upcoming IIS7.5 to give extra possibilities like extension-less URL even on hosted environment.


Categories: BlogEngine
Permalink | Comments (0) | Post RSSRSS comment feed

Drawing using JavaScript and jQuery

Drawing functionality is not supported in most of popular browsers except Firefox and Safari with Canvas object. Using little trick we can draw line and circle using JavaScript. This is not best practice because need lot of execution time but it can be alternative way to draw simple and little objects.

Basically this trick is drawing many DIV elements with 1x1 pixel size inside the document. Basically we can't draw 1x1 pixel suze DIV element in IE. To resolve this issue, you can add IMG element inside the DIV with 1x1 pixel image. Here's the complete code to draw line and circle into HTML document.

<html> <head> <title>Drawing</title> <script type="text/javascript" src="jquery-1.3.2.min.js"></script> <script type="text/javascript"> $(document).ready(function() { line(50,30, 100, 150); circle(110,110, 100); }); function line(x1, y1, x2, y2) { c = $(document.body); var dx = Math.abs(x2-x1); var dy = Math.abs(y2-y1); var d = Math.max(dx, dy); var i=0; for(i=0; i < d; i++) { var img = $(document.createElement('img')).attr('src', 'blank.gif'); var div = $(document.createElement('div')).width(1).height(1).css({'background-color': '#f00', position: 'absolute', left: Math.min(x1,x2)+(i*dx/d), top: Math.min(y1,y2)+(i*dy/d) }); div.append(img); c.append(div); } } function circle(x, y, r) { c = $(document.body); var l = 2 * Math.PI * r; var i=0; for(i=0; i < l * (1+((10-Math.log(r+1))/10)); i++) { var x2 = r * Math.sin(360 * i/l); var y2 = r * Math.cos(360 * i/l); var img = $(document.createElement('img')).attr('src', 'blank.gif'); var div = $(document.createElement('div')).width(1).height(1).css({'background-color': '#f00', position: 'absolute', left: x+x2, top: y+y2 }); div.append(img); c.append(div); } } </script> </head> <body> </div> </body> </html>

The result of rendered document is

image

I hope this trick is useful.


Categories: JavaScript
Permalink | Comments (2) | Post RSSRSS comment feed

How to Make Our Website to be Ready for IE8

Today, I have email from Microsoft TechNet Flash about "how to get ready for IE8". It is challenge for me to make my blog ready for IE8. My blog have already XHTML 1.0 Transitional and CSS 2.1 compliance, but still reported as not ready for IE8 (see picture below).

image

What happen with my blog? IE8 have difference standard with W3C? After following link from TechNet flash email, the problem is: my blog have not META tag to tell IE8 that the page is ready for IE8 view. After apply X-UA-Compatible META tag in my BlogEngine theme, my blog is now comply with IE8.

image

Here’s requirement to be ready for IE8.

  1. Make sure your HTML markup is comply to HTML 4, XHTML 1.0 or XHTML 1.1 defined in doctype of the page. You can validate your page with online markup validation tools at http://validator.w3.org/
  2. Make sure your CSS is use at least CSS level 2.1 compliance. You can validate your page with online CSS validation tools at http://jigsaw.w3.org/css-validator/
  3. Place the following HTML META tag into the top of the HEAD element of each Web page (before tags other than TITLE or META):
    <meta http-equiv="X-UA-Compatible" content="IE=8"/>
    This will tell Windows Internet Explorer 8 to render each page using CSS 2.1 standards.

After HTML, CSS and HTML META has been applied, the page will ready for IE8.

References:

http://msdn.microsoft.com/en-us/ie/cc405106.aspx
http://msdn.microsoft.com/id-id/library/cc817570(en-us).aspx
http://msdn.microsoft.com/id-id/library/dd433047(en-us,VS.85).aspx
http://msdn.microsoft.com/id-id/library/cc817572(en-us).aspx


Categories: Tips | IE
Permalink | Comments (3) | Post RSSRSS comment feed

Get Microsoft Certifications at 10% discount and free retake

top_banner
Staying Ahead in Your Career with Microsoft Certification

A challenging economic climate is driving many organizations to re-examine their operations. Ensuring employees have the skills to utilize the latest software capabilities helps companies fully realize the potential value of their technology investments while achieving reduced costs and improved staffing efficiencies.

As responsible IT professional, it is essential to stay current on the latest technologies and proactively upgrade one’s skills. This will help the team to provide the necessary expertise to improve the organization’s IT environment. Certification, which encompasses training and skills validation, is a proven way to maximize the effectiveness of IT professionals. Check out more information from TechRepublic on 10 reasons why certification will be important in 2009

How do you decide on the relevant Certification to invest in? Find out more on the demand of IT Skills and Certification available in the market. Some reports you can refer to include TechRepublic’s The industry's 10 best IT certifications, IT Skills@Work – An Asia Pacific Perspective by Kelly IT Resources (October 2008) and TechRepublic’s IT Salary Survey 2009.

This is the best time to join the
Microsoft Certified Professional community!
If you are interested in getting a Microsoft Certification, here’s the good news … go to www.learnANDcertify.com by April 30, 2009 and key in the Promo Code : ID4747BA to enjoy a 10% discount on your next Microsoft Certification exam. Even better, you will enjoy a free retake if you fail the exam. (Limited to Indonesia usage only)

Note : Once you decided on obtaining a Microsoft Certification, you can participate in the upcoming Live Meeting on “Getting Started with Microsoft Certification” on April 7, 2009 11:30pm Jakarta Time or April 8, 2009 9:30am Jakarta Time. Also you can visit www.learnANDcertify.com to find out more on the resources available.

Yours sincerely,
Ahmad Masykur
Microsoft ASP.NET MVP

Categories: Certification
Permalink | Comments (1) | Post RSSRSS comment feed

How To Create Silverlight Avatar for INDC blogs site

I have created Silverlight avatar for Community Server blog list page (http://geeks.netindonesia.net/blogs) a week ago as requested by Naren. I think its no difficult to replace HTML avatar with Silverlight avatar on the fly. Just query DOM using jQuery for existing avatar, grab photo Url information, hide it and replace with new Silverlight avatar. Here is step by step to create Silverlight avatar for CS theme on geeks site.

Create Silverlight project using Expression Blend.

I'm created two path to put avatar image and two StackPanel contain text-boxes for view counter text. Second path and StackPanel will mirrored-down to make shadow-mirror effect. These path will be binded by same image.

image

Here is the XAML for that design.

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="Avatar.Page"
    Width="133" Height="130">
    <Grid x:Name="LayoutRoot" Clip="M0,0 L133,0 L133,130 L0,130 z">
        <Grid.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FF8FC2E4"/>
                <GradientStop Color="#FFFFFFFF" Offset="1"/>
            </LinearGradientBrush>
        </Grid.Background>
        <Grid>
            <Canvas Margin="48 1 1 10" >
                <Path Data="M0,0 L82,0 L82,102 L0,91 z" Stretch="Fill" Stroke="#CCFFFFFF" >
                    <Path.Fill>
                        <ImageBrush x:Name="Photo" />
                    </Path.Fill>
                </Path>
                <Path Canvas.Top="193.5" Data="M1.5,12 L81,0 L81.75,73.5 L0.25,82.5 z" Stretch="Fill" Stroke="#CCFFFFFF" RenderTransformOrigin="0,0" Height="102.188" Width="82.5" Canvas.Left="0.125" >
                    <Path.OpacityMask>
                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.4,0">
                            <GradientStop Color="#AAFFFFFF" Offset="1"/>
                            <GradientStop Color="#00FFFFFF" Offset="0"/>
                        </LinearGradientBrush>
                    </Path.OpacityMask>
                    <Path.Fill>
                        <ImageBrush x:Name="PhotoShadow" />
                    </Path.Fill>
                    <Path.RenderTransform>
                        <TransformGroup>
                            <ScaleTransform ScaleY="-1"/>
                            <SkewTransform/>
                            <RotateTransform/>
                            <TranslateTransform X="0" Y="0"/>
                        </TransformGroup>
                    </Path.RenderTransform>
                </Path>
            </Canvas>
            <Canvas Margin="2,0,0,0">
                <Path Height="18" Width="134" Canvas.Left="-3" Canvas.Top="86.25" Data="M128,103.25 L-3,86.25" Fill="#FFFFFFFF" Stretch="Fill" Stroke="#B2FFFFFF"/>
                <StackPanel Canvas.Top="48" RenderTransformOrigin="0.5,0.5">
                    <StackPanel.RenderTransform>
                        <TransformGroup>
                            <ScaleTransform/>
                            <SkewTransform AngleY="6.972"/>
                            <RotateTransform/>
                            <TranslateTransform Y="4.006"/>
                        </TransformGroup>
                    </StackPanel.RenderTransform>
                    <TextBlock x:Name="ViewCount" Text="9999" TextWrapping="Wrap" FontFamily="Arial" FontSize="20" Foreground="#FF023D9E" HorizontalAlignment="Right"/>
                    <TextBlock Text="Views" TextWrapping="Wrap" FontFamily="Arial" FontSize="16" Foreground="#FF023D9E" HorizontalAlignment="Right"/>
                </StackPanel>
                <StackPanel Canvas.Top="84" RenderTransformOrigin="0.5,0.5">
                    <StackPanel.OpacityMask>
                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                            <GradientStop Color="#00FFFFFF"/>
                            <GradientStop Color="#AAFFFFFF" Offset="1"/>
                        </LinearGradientBrush>
                    </StackPanel.OpacityMask>
                    <StackPanel.RenderTransform>
                        <TransformGroup>
                            <ScaleTransform ScaleY="-1"/>
                            <SkewTransform AngleY="7.585"/>
                            <RotateTransform/>
                            <TranslateTransform Y="3.288"/>
                        </TransformGroup>
                    </StackPanel.RenderTransform>
                    <TextBlock x:Name="ViewCountShadow" Text="9999" TextWrapping="Wrap" FontFamily="Arial" FontSize="20" Foreground="#FF023D9E" HorizontalAlignment="Right"/>
                    <TextBlock Text="Views" TextWrapping="Wrap" FontFamily="Arial" FontSize="16" Foreground="#FF023D9E" HorizontalAlignment="Right"/>
                </StackPanel>
            </Canvas>
        </Grid>
    </Grid>
</UserControl>

Write code-behind to bind avatar image and view counter.

This code-behind used to bind avatar image and view counter. The image url and view counter passed by InitParams property from javascript. The App.xaml.cs code-behind used to get InitParams and Page.xaml.cs used to bind the image and text from parameter values.

App.xaml.cs

 1 using System.Windows;
 2 using System;
 3 using System.Windows.Browser;
 4 
 5 namespace Avatar
 6 {
 7     public partial class App : Application 
 8     {
 9         public App() 
10         {
11             this.Startup += this.OnStartup;
12             this.Exit += this.OnExit;
13             this.UnhandledException += this.Application_UnhandledException;
14 
15             InitializeComponent();
16         }
17 
18         private void OnStartup(object sender, StartupEventArgs e)
19         {
20             var page = new Page();
21             // Read application parameter
22             var imageSourceUrl = string.Empty;
23             int viewCountInteger = 0;
24             if (e.InitParams.Count > 0)
25             {
26                 var viewCount = string.Empty;
27                 if (e.InitParams.ContainsKey("ImageSourceUrl"))
28                     imageSourceUrl = HttpUtility.HtmlDecode(e.InitParams["ImageSourceUrl"]);
29                 if (e.InitParams.ContainsKey("ViewCount")) viewCount = e.InitParams["ViewCount"];
30                 // Load the main control here
31                 if (!string.IsNullOrEmpty(viewCount))
32                     int.TryParse(viewCount, out viewCountInteger);
33             }
34             
35             page.SetImageSource(imageSourceUrl);
36             page.SetViewCount(viewCountInteger);
37             this.RootVisual = page;
38         }
39 
40         private void OnExit(object sender, EventArgs e) 
41         {
42         }
43 
44         private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) 
45         {
46             // If the app is running outside of the debugger then report the exception using
47             // the browser's exception mechanism. On IE this will display it a yellow alert 
48             // icon in the status bar and Firefox will display a script error.
49             if (!System.Diagnostics.Debugger.IsAttached)
50             {
51                 // NOTE: This will allow the application to continue running after an exception has been thrown
52                 // but not handled. 
53                 // For production applications this error handling should be replaced with something that will 
54                 // report the error to the website and stop the application.
55                 e.Handled = true;
56                 Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); });
57             }
58         }
59 
60         private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e)
61         {
62             try
63             {
64                 string errorMsg = e.ExceptionObject.Message + @"\n" + e.ExceptionObject.StackTrace;
65                 errorMsg = errorMsg.Replace("\"", "\\\"").Replace("\r\n", @"\n");
66 
67                 System.Windows.Browser.HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight 2 Application " + errorMsg + "\");");
68             }
69             catch (Exception)
70             {
71             }
72         }
73     }
74 }

 

Page.xaml.cs

 1 using System;
 2 using System.Windows;
 3 using System.Windows.Browser;
 4 using System.Windows.Controls;
 5 using System.Windows.Media.Imaging;
 6 
 7 namespace Avatar
 8 {
 9     public partial class Page : UserControl
10     {
11         public Page()
12         {
13             // Required to initialize variables
14             InitializeComponent();
15             
16         }
17         public void SetImageSource(string imageSourceUrl)
18         {
19             if (!string.IsNullOrEmpty(imageSourceUrl))
20             {
21                 var url = new Uri(imageSourceUrl, UriKind.RelativeOrAbsolute);
22                 if (!url.IsAbsoluteUri)
23                 {
24                     url = new Uri(HtmlPage.Document.DocumentUri, url);
25                 }
26                 var image = new BitmapImage(url);
27                 Photo.ImageFailed += Photo_ImageFailed;
28                 PhotoShadow.ImageFailed += Photo_ImageFailed;
29                 Photo.ImageSource = image;
30                 PhotoShadow.ImageSource = image;
31             }
32         }
33         public void SetViewCount(int viewCount)
34         {
35             ViewCount.Text = viewCount.ToString();
36             ViewCountShadow.Text = viewCount.ToString();
37         }
38         void Photo_ImageFailed(object sender, ExceptionRoutedEventArgs e)
39         {
40         }
41     }
42 }

 

Note: You must get absolute Url for downloading photo with query string, see line 21-25 above. Silverlight will ignored query string if you are not provide absolute url for image source.

 

Grab DOM using jQuery

jQyery is very powerful tool for DOM manipulation. You can query and modify DOM with single line of code. Before query the DOM, you should be knows the DOM structure of page. Target of DOM is the avatar image. Open IE development toolbar to get the DOM of avatar. Find ID or CSS classes that used by Avatar to be replaced by Silverlight avatar.

image

From this information you can analyze that every blog entry placed inside DIV element with content_main_con_be CSS class. This DIV element contain view count and avatar image DIV in first and second child element. Based on this information, you can query the DOM using jQuery as script in line number 11 and 12 below.

 1 /*
 2     Filename: avatar.js
 3     Project : INDC Geeks
 4     Ver.    : 1.0
 5     Date    : March, 4th 2009
 6     Author  : Ahmad Masykur - www.masykur.web.id
 7 */
 8 
 9 $(document).ready(function() {
10     if (Silverlight.isInstalled('2.0')) {
11         var viewCountElements = $("div.content_main_con_be div:nth-child(1) > span:even");
12         var pictureBoxes = $("div.content_main_con_be div:nth-child(2)")
13         pictureBoxes.each(function(i, element) {
14             $(element).width(132).height(130);
15             var imageSourceUrl = $('img', element).attr('src');
16             createSilverlight(element, imageSourceUrl, $(viewCountElements[i]).text());
17         });
18         $("div.content_main_con_be div:nth-child(1)").hide();
19     }
20 });
21 
22 function createSilverlight(host, imageSourceUrl, viewCount) {
23     Silverlight.createObject(
24         '/ClientBin/Avatar.xap?v=0.2',
25         host, host +"_Plugin",
26         {
27             width: "100%", height: "100%",
28             background: "#E8CFB0", 
29             version: "2.0.31005.0" 
30         },
31         { onLoad: null },
32         'ImageSourceUrl=' + imageSourceUrl + ', ViewCount=' + viewCount);
33 }
34 

Description of script above are.

  • The script above will be execute when DOM is ready (evaluated in line 9).
  • Line 10 will verify if Silverlight 2 is installed in the machine. If Silverlight 2 is installed, DOM will be replaced with Silverlight object.
  • Line 11 and 12 is getting view counter and picture box elements.
  • Line 13: Make a loop the selected DOM. 
  • Line 14: Set the width and height of element that will be used for Silverlight host
  • Line 15: Get image Url information from IMG element
  • Line 16: Create Silvelight object using javascript.
  • Line 22-33 is function to create Silverlight object.
  • Line 32: Set InitParams value to be passing to Silverlight application.

This script required Silverlight.js and jQuery 1.3.2. The last steps are upload xap file of Silverlight application and required scripts also included the script above, Silverlight.js and jquery-1.3.2.min.js into blog.master of theme.

<script src="/JavaScripts/Silverlight.js" type="text/javascript"></script> 
<script src="/JavaScripts/jquery-1.3.2.min.js" type="text/javascript"></script>
<script src="/JavaScripts/avatar.js" type="text/javascript"></script>

Finally, the geeks portal have a new theme with Silverlight avatar. This avatar will available both installed Silverlight plung-ins or not. User will see HTML version of avatar if they don't have Silverlight plug-ins installed on the machine.

image

I would like to say thank you to Wely and Naren that support me to make it running well in geeks portal.


Categories: Silverlight
Permalink | Comments (3) | Post RSSRSS comment feed

Parsing Array Parameter in SQL Server Stored Procedure

If we develop application to select multiple data in grid and selected data to be process in stored procedure at same time, we need to pass the values of keys to stored procedure parameters. The problem is SQL Server has not array parameter. Here is some ways to resolve the problem.

  1. Use EXEC to execute dynamic query and passing the stored procedure parameter in WHERE clause.
  2. 2. Use XML as parameter and using OPENXML keyword to convert it to table.

First option is vary simple but it bad practice to use dynamic query. SQL Server can’t save statistic of dynamic query. Second option is better way but more complicated. We need simplest way to passing an array to stored procedure parameter.

Other option we can create table function to parse delimited string to table. Here is sample function to do that.

CREATE FUNCTION [dbo].[fn_ParseDelimitedStrings](@String nvarchar(MAX), @Delimiter char(1))
RETURNS @Values TABLE
(
     RowId int Not Null IDENTITY(1,1) PRIMARY KEY
    ,Value nvarchar(255) Not Null
)
AS
BEGIN
    DECLARE  @startPos smallint
            ,@endPos smallint

    IF (RIGHT(@String, 1) != @Delimiter)
        SET @String = @String + @Delimiter
    SET @startPos = 1
    SET @endPos = CharIndex(@Delimiter, @String)
    WHILE @endPos > 0
        BEGIN
            INSERT @Values(Value)
            SELECT LTRIM(RTRIM(SUBSTRING(@String, @startPos, @endPos - @startPos)))
            -- remove the delimiter just used
            SET @String = STUFF(@String, @endPos, 1, '')
            -- move string pointer to next delimiter
            SET @startPos = @endPos
            SET @endPos = CHARINDEX(@Delimiter, @String)
    END
    RETURN
END

This function can be used in sample stored procedure below.

CREATE PROCEDURE GetCustomers 
    @customerIDs AS nvarchar
AS
BEGIN
    DECLARE @custIDs AS table (Value nvarchar(255) Not Null)
    INSERT INTO @custIDs
        SELECT Value FROM [fn_ParseDelimitedStrings](@customerIDs, ',') 
    SELECT * FROM Orders WHERE CustomerID IN (SELECT Value FROM @custIDs)
END

It very simple query. Just declare memory table and parse the delimited string into table to be used in select criteria.

I hope this tips is helpful for you.


Categories: SQL Server | Tips
Permalink | Comments (7) | Post RSSRSS comment feed