Monthly Archives: June 2008

AjaxControlToolkit Script Browser

When creating my last post I tracked down all of the JavaScript files the ModalPopup Toolkit control loads.  I did this manually by using FireBug to see what script files were pushed out to the client and then I used Reflector to find out the size of the file (the Toolkit embeds the JavaScript files as resources).  That was tedious.  But I found the information pretty useful (I wouldn't have guessed the ModalPopup required the RoundedCorners script) and so I thought it might be useful to view this information for all of the Toolkit controls.

So I cooked up a simple console application that rips through the Toolkit's assembly (I used version 1.0.20229.0) and tracks down all of the ClientScriptResources a particular control is using.  As I identified the scripts I ran then each through the minify -> gzip process so I could better gauge how large these scripts are when compared to jQuery and some of its plug-ins.  A few things to note …

  • Unless I am mistaken, the Toolkit scripts were minified before they were embedded into the Toolkit's assembly.  I don't see any comments in the source code or anything so I am assuming this is correct.  Does anyone know what tool was used here?
  • I also downloaded the YUI Compressor (another minifier) to see how much better it could do than what comes by default
  • I did the same with JSMin
  • Finally to simulate HTTP compression, I gzipped all of the minified files to get a closer approximation to the size of the file that is actually transmitted

Below is what it looks like (I fit the output into one of my grouping grid’s).  So you can browse the Toolkit controls and check out both how many scripts the control depends on and what the overall compressed size of the script is (i.e. the ModalPopup compressed is only 22 KB – much smaller than the original 102 KB).

Live Demo | Download

image

That's it.  Enjoy!

Master-Detail with the GridView, DetailsView and jQuery’s ThickBox

And so I continue on messing around with jQuery and looking into ways I can use it in my WebForms applications.  Something I sheepishly admitted to in my last post was that I don’t want to use 3 client side libraries {ASP.NET AJAX, AjaxControlToolkit, jQuery}, yet I never really took the time to see what the size of the core jQuery library is.  And for that matter I haven't really looked at what scripts some of my favorite AjaxControlToolkit controls are pulling down either.  I also assumed there is a bunch of overlap between jQuery and ASP.NET AJAX, but I haven't looked into that either. 

So I thought I would check some of this out and along the way rebuild my Master-Detail with the GridView, DetailView and ModalPopup Controls and replace the ModalPopup with jQuery’s ThickBox.  I found it pretty interesting – read on to see how it went.

Live Demo | Download

ThickBox Demo

What I Was Looking For

While building this sample I decided I would keep my eye out for the following things …

  1. Size Comparison (minified, but not gzipped) of AjaxControlToolkit’s ModalPopup versus jQuery’s ThickBox
  2. Installation / Configuration 
  3. Programming Model / API 
  4. ASP.NET AJAX Integration 

 

jQuery’s ThickBox

If you are not familiar with jQuery’s ThickBox = here the 2 sentence description taken from the ThickBox home page

ThickBox is a webpage UI dialog widget written in JavaScript on top of the jQuery library.  Its function is to show a single image, multiple images, inline content, iframed content, or a content server through AJAX in a hybrid model

Size Comparison

This is what I was most interested in.  I get a lot of comments to my AjaxControlToolkit posts that comment on how large the client side footprint of the Toolkit is.  And I honestly don't really know how to respond – mostly because I don't have a baseline that I can compare it to.  And jQuery's ThickBox and the Toolkit's ModalPopup is not exactly like comparing apples to apples, but I think it is close enough to get a feeling for things.

So here are the numbers …

  File Size (Compressed)
ThickBox    
  jQuery.js 55KB
  ThickBox.js/ThickBox.css 10KB
Total   65KB
ModalPopup    
  Common.js 28KB
  BaseScripts.js 11KB
  RoundedCornersBehavior.js 27KB
  Timer.js 1KB
  DropShadowBehavior.js 7KB
  DynamicPopulateBehavior.js 6KB
  DragDropScripts.js 23KB
  DynamicPopulateBehavior.js 6KB
  Floatingehavior.js 4KB
  ModalPopupBehavior.js 18KB
Total   131KB

 

The ModalPopup and its supporting scripts is just over twice the size of the jQuery plus ThickBox combo.  Interesting.  If you are curious about where I got the numbers from – I used the file system to get the jQuery/ThickBox numbers and Reflector to look at the sizes of the Toolkit's supporting JavaScript files (they are embedded resources). 

image

I should mention though that although both the ModalPopup and ThickBox JavaScript files are both minified – this wasn't done with the same tool.  And to my novice eye, it looks like the one jQuery uses does a better job.  So you should keep this in mind when interpreting these numbers.  Also, this is without gzipping – so these numbers will both be smaller after this is applied as well.

 

ThickBox Installation and Configuration

If you want to use the ThickBox, you have to do the following things …

1.  Download the latest version of jQuery and add a script reference to your page

2.  Download thickbox.js, thickbox-compressed.js, thickbox.css and the loadingAnimation.gif graphic and place them in your web site.  I included both the compressed and non-compressed versions of thickbox just in-case I wanted to debug any issues I ran into. 

After steps #1 and #2, your site should look something like this (the highlighted are the jQuery/ThickBox components) …

image

And your page will include the following JavaScript and CSS references.

image

3.  Next, you need to update the thickbox-compressed.js and thickbox.js files to include the location of the loading graphic you would like to display while the ThickBox is loading.  The ThickBox component displays an animated gif while the ThickBox is being displayed.  By default the component assumes the location of this graphic is images/loadingAnimation.gif.  So if this isn't the location on your web server you will have to manually update the thickbox.js and thickbox-compressed,js files and update this variable.  In my app, I am storing the graphic at _assets/img/loading.gif so I updated the thickbox JavaScript files to use this path instead of the default one.

//  change this ...
var tb_pathToImage = "images/loadingAnimation.gif";

//  to this ...
var tb_pathToImage = "_assets/img/loading.gif";

 

 

Programming Model

The ThickBox programming model works as follows …

You add an A or INPUT to your page and adorn it with the thickbox CSS class.  Then, like magic, when the element is clicked the ThickBox is displayed.  Here is a real simple example that shows an image in a ThickBox – when the A is clicked the nested IMG element is displayed in a ThickBox.


Single Image

 

The ThickBox also supports other scenarios too – like displaying some in-line content from the page inside the ThickBox (like a hidden DIV or something) as well as showing an IFRAME in the ThickBox.  These other scenarios require some additional parameters – and the ThickBox uses some of the existing INPUT and A attributes to specify these values …

  • title – you can use the A or INPUT’s title attribute to specify a title for the ThickBox
  • A.href and INPUT.alt – the value of this attribute is parsed into some of the parameters that control how the ThickBox behaves

For example, here is how you would use the ThickBox to display some hidden content on your page…

  • The href attribute of the A element contains the encoded parameters
  • #TB_inline tells the ThickBox that the content to be displayed exists some where on the current page
  • height/width specify the height and width of the ThickBox
  • inlineId is the ID of the element that will be displayed
  • modal specifies that the ThickBox will be displayed modally
Show hidden modal content.

 

And here is a very simple example of an anchor that when clicked displays some hidden content.

Show hidden content.

 

And here is what it looks like.  In this sample, the dialog isn't modal so it can be closed via the Close or pressing the Escape key or just by clicking any where else on the page.

image

 

ASP.NET AJAX Integration

And now of the real question – how well does the ThickBox work with my WebForms apps?  Well, I tried porting over my previous sample of using the ModalPopup to edit rows in a GridView.  Here is how it went …

  • I started down the path of using the ThickBox to display some hidden inline content, but that didn't work out to well.  The ThickBox takes the contents of the inline content and moves the DOM elements out to an immediate child of the document's BODY element.  That doesn't work too well when used with WebForms because I need everything contained within the FORM element for my page to work properly. 

Check it out – below is what the DOM looks like.  See how the FORM element and the TB_window DIV are siblings – that isn't going to work. 

image

But this isn't exactly a show stopper either – I can still move my detail content over to a separate page and have the ThickBox load it up using an IFRAME.  So that's were I went next.

  • I updated the markup for my main GridView and changed the last column to render an A tag with all of the parameters I need to tell the ThickBox to load up a new page in an IFRAME.  Remembering how the ThickBox parses the arguments from the anchor's href and title attributes, I used a databinding expression to build out the values for these attributes. 

When the dialog shows I want to include the Customer name in the title, so I am using the ContactName property to build the title value.  I also want to show the Detail.aspx page passing the current rows ID value as an argument.  So I again use a databinding expression to set the href attribute to the value I want.


    
        
        
        
                        
                        
                        
        
            
                Edit                
            
                                
                        
  • Next, I created the Detail.aspx page that displays the detailed information for a given Customer.  This page requires a single parameter, ID, that passed via the querystring.  This page displays the Customer detail information in a DetailsView and allows you to edit the values.  The page has a single Save button that when clicked ensures the page is valid, saves the changes back to the data store, and closes the ThickBox window.

The interesting part here is that the Detail page is responsible for closing itself after the user presses the Save button.  So to accomplish this, I need to register a small script that will run after the Save has completed that will close the ThickBox.  To get this bit of work done, I first created a JavaScript function on the main page called updated that closes the ThickBox window using the ThickBox’s tb_remove function.  Next, I need to let the main grid know that it needs to refresh itself (otherwise you wouldn't see any of the changes you just made).  I handle this by setting up a hidden Refresh button that when clicked causes the main grid to refresh itself.

Here is what the updated function looks like … 

function updated() {
    //  close the popup
    tb_remove();
    
    //  refresh the update panel so we can view the changes  
    $('#<%= this.btnRefreshCustomers.ClientID %>').click();      
}

And here is what the Detail page’s Save event handler looks like …

protected void BtnSave_Click(object sender, EventArgs args)
{
    if (this.Page.IsValid)
    {
        //  move the data back to the data object
        this.dvCustomerDetail.UpdateItem(false);
        
        //  register the script to close the popup
        this.Page.ClientScript.RegisterStartupScript(typeof(detail_aspx), "closeThickBox", "self.parent.updated()", true);
    }
}

 

  • And I actually need make one more modification.  My customer grid is contained within an UpdatePanel.  And even though this grid doesn't sorting or paging, the example as-is wouldn't work too well after the user triggered an event that causes the UpdatePanel to refresh itself.  The reason why is because the ThickBox works by fetching all of the A and INPUT's that contain the thickbox class – and this selection occurs just after the page is initially loaded.  And it doesn't happen again after that.  So we need to add a special piece of logic that will reapply the ThickBox to the A elements that are contained within our UpdatePanel.  And here is the bit of JavaScript that I am using to do this …
function pageLoad(sender, args) {
    if(args.get_isPartialLoad()){
        //  reapply the thick box stuff
        tb_init('a.thickbox');
    }
}

 

This is again scanning the complete page for anchor elements with the thickbox class and adding the ThickBox behavior to them.  This bit of logic could be optimized because …

  • We don't need to scan the complete page again, just inside any UpdatePanels that were refreshed.  This is where it might be useful to use a Sys.Component to auto-magically keep track of this stuff for us.  We could easily hook into some of the ASP.NET client side page events and reapply the ThickBox stuff to any elements that contained within an UpdatePanel.  This is pretty similar to the approach I used here to maintain scroll position across partial post-backs.    

 

Conclusion

So, after all of this, here are a few things I found interesting.

  • When I saw all of the scripts the ModalPopup required I couldn't help think it might be interesting if there was a way for the control to only render the scripts it needed to function based on the features you are currently using.  That would be useful.
  • It took me a while to learn the ThickBox's API.  Encoding the parameters into the href is crafty, but it also makes the features hard to discover without documentation.  The Toolkit has a very consistent API.
  • None of the jQuery stuff is going to have a server side API – so when using jQuery from my WebForms I am going to be injecting quite a bit more JavaScript from the page’s codebehind than I am used to.
  • I didn't like how the ThickBox forces me to separate my detail widget into a separate page.  But the reality is that most of the jQuery stuff is not written with WebForms in mind.  So running into this stuff might be the norm when trying to use these scripts.

Also, keep in mind this is just one sample.  In my mind, this doesn't tell me to replace the Toolkit with jQuery or vice versa. 

 

That's it.  Enjoy!   

Episode 56 – Tablesaw Setup/Tuneup (Pt. 2)

Download Low Resolution
Download High Resolution

In the second and final part of our tablesaw setup series, we continue our quest for fine tuning. Topics include setting the blade bevel angle, adjusting fence alignment with the blade, adjust fence so that it is square to the table, setting up the insert, and lining up the splitter.

And at the end, you will get a nice full view of the new saw. For those who are curious, it is a PM Custom from the folks at Wood Werks Supply.

Toy Box/Blanket Chest – Project of the Week

This week’s project comes from Robert who writes:

This is my first attempt at building furniture. I decided to just dive in as a learning experience. The blanket chest was inspired by a Fine Wood Working article by John Mcalevey (March/April 1998).The chest was built for my two and a half year old granddaughter to serve as aw toy box now and hope chest later. Cherry and soft maple. Thanks for all the help I have found on your web site.


Run Windows Apps in Linux with Wine 1.0 [Feature]


No matter how easy Linux distributions make it for newcomers to install and use a free, open-source operating system, nearly everyone has at least one program that only works in Windows. Wine, a free Windows compatibility tool for Linux (and other Intel-based systems), aims to make those programs run without too much cross-system trickery. If you can’t get around needing to open true Microsoft Office files, Adobe Photoshop, or your addictive game of choice on your Linux desktop, Wine is for you. With Wine’s stable 1.0 version just released, it’s a good time to check out this quietly awesome app. Let’s get a few Windows applications running in Linux.

Wait, before I try this, will X program run okay in Wine?

apps_cropped.pngGood question—luckily, there’s probably an answer. The Wine AppDB lists all the programs that run and don’t run under Wine, and to what degrees. You’ll see rankings randing from “Platinum” (runs pretty much flawlessly) to “Bronze” (some functions may not work at all, but otherwise runs) to “Garbage” (don’t bother). In general, any apps that rely on other Windows apps or functions, or interact with the Windows desktop, won’t work as well, if at all. That means Adam’s super-useful Texter app doesn’t work in Wine, for example.

The big wins for Wine are Microsoft Office viewers (and, in some cases, full applications) up through 2003 versions, Adobe Photoshop CS2, and many games, including World of Warcraft. You can even get iTunes playing and purchasing music, but no iPod syncing (yet). Assuming you’ve got something you want to try out, let’s get started.

Install and configure Wine

Most Linux distributions have Wine available in their repositories, but usually a bit out of date. You can grab an easy-install package or repository instructions at Wine’s download site. (Mac users with Intel processors should check out Darwine, which keeps up with the official Wine releases. Here’s a guide to setting it up).

Wine usually installs itself, creates a .wine folder in your home directory, and uses that as a pretend “C:/” drive to place your applications and needed files in. If not, enter winecfg into a terminal and it’ll do the work and bring up its configuration screen.

desktop_integration.pngHead first to the “Desktop Integration” tab. If you want your Wine/Windows apps to treat the “Desktop” or “My Documents” save locations the same as your Linux desktop or documents folders, change the values to point at the right locations. Head next to “Drives,” where a click on “Autodetect” should set up a group of virtual drives for your Wine apps (your home folder becomes “H:” for example, and folders inside your .wine folder become other drives). You can click on “Sound” to see if Wine is picking up your Linux driver well enough. That should be good enough for now—let’s install our first app.

All three of Microsoft’s major Office creator tools—Word, Excel, and PowerPoint—have free “viewers” that let anyone without a full Office suite open and copy data from Office documents. All three of the 2003 versions happen to run great in Wine, making them pretty helpful to anyone who simply needs to occasionally see or copy from a document or spreadsheet with tricky formatting. Grab a copy of the Word, Excel, or PowerPoint viewers, then open a terminal and change directories (cd to wherever you put the .exe installer file).

pptview_install.pngNow enter wine whatever.exe (or, in certain systems, right-click on the .exe file and choose to launch with Wine) and you’ll get a familiar-looking prompt to install, followed by a license agreement, then a quick installation and goodbye. Head to Wine->Programs in your system menu, or browse to the viewer in your pretend “Program Files” folder (manually or by choosing “Browse C: Drive” from the Wine menu).

powerpoint_in_wine.pngYou should see your viewer program, and it will likely launch with no problems. If you wanted to go further into full Office apps, you can either take your chances with straight-up Wine—where you might see “Platinum” and “Garbage” reports for the same Office version on the same Linux system—or invest in a Crossover product for about $40, which gives near-guaranteed sucess with Office apps.

foobar.pngYou can change which version of Windows Wine attempts to recreate in the “Applications” tab in configuration. Browse and select an app, choose a Windows version in the lower-right, then add any DLLs you might need or graphics settings that need tweaking in the “Libraries” and “Graphics” tabs. Programs like AutoStitch, Foobar2000, and even Windows automation tool AutoHotKey install just fine with no tweaking, but your mileage may vary. If you do run into errors regarding missing DLLs/libraries, try installing the winetricks script, which automatically seeks out commonly missing .dlls for installed programs.

The big caveat of Wine is that there are so many Windows programs out there, and myriad ways they can go wrong on installation or launch. A catch-all installation and troubleshooting method doesn’t exist; you’re best off looking in the AppDB, Googling your troublesome app with “wine linux” after it, or just tinkering with your Wine settings until you find out what’s causing the bug.

Taking Wine further

Once you’ve gotten started using your apps in Wine, it’s time to make them a better fit with your desktop. Here’s a few tips and suggestions on making Wine an almost invisible layer in Linux:

  • Keep Linux file associations: There’s probably no need to install a Windows-based PDF reader just because another Wine app wants access to one, so you need to teach Wine to reach for what you’ve already got available. Check out this wiki entry for details on choosing which apps open which files in your pseudo-Windows environment.
  • wine_fix_cropped.jpgDe-uglify the interface: Wine takes an OS-neutral, Java-like approach to drawing buttons, tabs, and other implements on its windows, but wouldn’t it be nice if they actually meshed with your Linux desktop? The Tombuntu blog shows you how, from a simple text file copy to more extensive (and memory-eating) tweaks (Original post).
  • Uninstall programs: Got a bit too much clutter in your Wine menu after a few try-outs? Simply type uninstaller into a terminal, and you’ll get a simple click-and-choose interface that activates the familiar Windows uninstaller (“Are you sure you’d like to …”).

If you’re looking for more help on customizing and running Wine apps, I’d recommend, in addition to Wine’s own wiki, these two sites:

  • Frank’s Corner: A one-man Wine guru, Frank offers up specific hints and tricks on wrangling Office, multimedia, gaming, and other Windows apps. Slightly behind-the-times, but his advice still holds.
  • Ubuntu’s Wine wiki page: For users of the orange-tinted distro, a few handy how-tos and guidance on what can and can’t be done with Wine.

Veteran Wine users, what favorite apps have you rescued from other-partition exile? Newcomers, what other resources can you point out for getting more Windows apps running ever-so-smoothly in Linux? Share and share alike in the comments.

Kevin Purdy, associate editor at Lifehacker, waits with bated breath for iTunes to get iPod support in Wine. His weekly feature, Open Sourcery, appears Fridays on Lifehacker.

W-4 Assistant Takes the Guesswork Out of First-Day Forms [Work]

w4_assistant.pngAs simple as it may seem at a glance, the W-4 form, used to determine how much comes out of a regular paycheck, can be downright confusing to first-time job getters, or anyone who hasn’t filled one out in a while. The PaycheckCity website has a helpful guide that determines what you’ll list for dependents, allowances, and all the other boxes, with pop-up help available for each step. PaycheckCity’s home page also has similarly useful calculators for determining what your paycheck or hourly wage will look like after taxes and deductions—not a bad bookmark for the job-seeking set.


Pushing Data to a Silverlight Client with a WCF Duplex Service – Part II

In Part 1 of this series on pushing data to a Silverlight client with a WCF polling duplex service I demonstrated how service contracts and operations can be defined on the server.  WCF has built-in support for duplex communication (two-way communication between a service and a client) but does require a reference to System.ServiceModel.PollingDuplex.dll to make it work with Silverlight.  This assembly is provided in the Silverlight SDK and is currently in “evaluation” mode (the Silverlight go-live license doesn’t apply to it).  With the polling duplex model the Silverlight client does poll the service to check if any messages are queued so it’s not as “pure” as the sockets option available in Silverlight when it comes to pushing data from a server to a client.  However, it offers much greater flexibility when compared to sockets since it isn’t limited to a specific port range and works over HTTP.

Let’s take a look at how a Silverlight client can send and receive messages from a polling duplex WCF service and what types of messages are sent between the two.

Understanding Polling Duplex Messages

A polling duplex service communicates with a Silverlight client using WCF Message types.  This provides complete control over the data sent between the client and the service and allows communication between the two to be loosely coupled.  The downside of this is that messages must be manually serialized/deserialized by the client and service since the WSDL type information uses the xs:any element.  Here’s what the service’s WSDL types section looks like (notice the inclusion of the xs:any element) when a service uses the Message type as a parameter for an operation:



  
    
  

An example of using the WCF Message type in a WCF service is shown next.  Details about this code were covered in Part I of this series.

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;

namespace WCFPushService
{
    public class GameStreamService : IGameStreamService
    {
        IGameStreamClient _Client;
        Game _Game = null;
        Timer _Timer = null;
        Random _Random = new Random();

        public GameStreamService()
        {
            _Game = new Game();
        }

        public void GetGameData(Message receivedMessage)
        {

            //Get client callback channel
            _Client = OperationContext.Current.GetCallbackChannel<IGameStreamClient>();

            SendData(_Game.GetTeamData());
            //Start timer which when fired sends updated score information to client
            _Timer = new Timer(new TimerCallback(_Timer_Elapsed), null, 5000, Timeout.Infinite);
        }

        private void _Timer_Elapsed(object data)
        {
            SendData(_Game.GetScoreData());
            int interval = _Random.Next(3000, 7000);
            _Timer.Change(interval, Timeout.Infinite);
        }

        private void SendData(object data)
        {
            Message gameDataMsg = Message.CreateMessage(
                MessageVersion.Soap11,
                "Silverlight/IGameStreamService/ReceiveGameData", data);

            //Send data to the client
            _Client.ReceiveGameData(gameDataMsg);
        }
    }
}

Creating a Silverlight Duplex Polling Receiver Class

Calling and receiving data in Silverlight requires a fair amount of code to be written.  Before showing the code to interact with a polling duplex service it’s important to understand the general steps involved.  Here’s what you need to do to send and receive data in a Silverlight client:

Reference Assemblies and Namespaces

  1. Reference System.ServiceModel.dll and System.ServiceModel.PollingDuplex.dll in your Silverlight project.  Additional details on where to find the System.ServiceModel.PollingDuplex.dll assembly used by Silverlight can be found here.
  2. Import the System.ServiceModel and System.ServiceModel.Channels namespaces.

Create a Factory Object

  1. Create a PollingDuplexHttpBinding object instance and set the PollTimeout and InactivityTimeout properties (both were discussed in Part 1).
  2. Use the PollingDuplexHttpBinding object to build a channel factory.
  3. Open the channel factory and define an asynchronous callback method that is called when the open completes.

Create a Channel Object

  1. Use the factory class to create a channel that points to the service’s HTTP endpoint.
  2. Open the channel and define an asynchronous callback method that is called when the open completes.
  3. Define a callback method that is called when the channel closes.

Send/Receive Messages

  1. Create a Message object and send it asynchronously to the service using the channel object.  Define an asynchronous callback method that is called when the send completes.
  2. Start a message receive loop to listen for messages “pushed” from the service and define a callback method that is called when a message is received.
  3. Process data pushed by the server and dispatch it to the Silverlight user interface for display.

Now that you’ve seen the fundamental steps, let’s take a look at the code that makes this process work.  The following code shows a class named PushDataReceiver that encapsulates the factory and channel classes and handles all of the asynchronous operations that occur.  The class allows an object of type IProcessor to be passed into it along with a service URL, service action and initial data to send to the service (if any).  The IProcessor object represents the actual Silverlight Page class used to update data on the user interface in this case.  As data is received the Page class’s ProcessData() method will be called.

using System;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;
using System.IO;
using System.Xml.Serialization;

namespace SilverlightPushClient
{
    public interface IProcessor
    {
        void ProcessData(object receivedData);
    }

    public class PushDataReceiver
    {
        SynchronizationContext _UiThread = null;
        public IProcessor Client { get; set; }
        public string ServiceUrl { get; set; }
        public string Action { get; set; }
        public string ActionData { get; set; }

        public PushDataReceiver(IProcessor client, string url, string action, string actionData)
        {
            Client = client;
            ServiceUrl = url;
            Action = action;
            ActionData = actionData;
            _UiThread = SynchronizationContext.Current;
        }

        public void Start()
        {
            // Instantiate the binding and set the time-outs
            PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding()
            {
                PollTimeout = TimeSpan.FromSeconds(10),
                InactivityTimeout = TimeSpan.FromMinutes(1)
            };

            // Instantiate and open channel factory from binding
            IChannelFactory<IDuplexSessionChannel> factory =
                binding.BuildChannelFactory<IDuplexSessionChannel>(new BindingParameterCollection());

            IAsyncResult factoryOpenResult =
                factory.BeginOpen(new AsyncCallback(OnOpenCompleteFactory), factory);
            if (factoryOpenResult.CompletedSynchronously)
            {
                CompleteOpenFactory(factoryOpenResult);
            }
        }

        void OnOpenCompleteFactory(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteOpenFactory(result);
        }

        void CompleteOpenFactory(IAsyncResult result)
        {
            IChannelFactory<IDuplexSessionChannel> factory =
                (IChannelFactory<IDuplexSessionChannel>)result.AsyncState;

            factory.EndOpen(result);

            // The factory is now open. Create and open a channel from the channel factory.
            IDuplexSessionChannel channel =
                factory.CreateChannel(new EndpointAddress(ServiceUrl));

            IAsyncResult channelOpenResult =
                channel.BeginOpen(new AsyncCallback(OnOpenCompleteChannel), channel);
            if (channelOpenResult.CompletedSynchronously)
            {
                CompleteOpenChannel(channelOpenResult);
            }
        }

        void OnOpenCompleteChannel(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteOpenChannel(result);
        }

        void CompleteOpenChannel(IAsyncResult result)
        {
            IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;

            channel.EndOpen(result);

            // Channel is now open. Send message
            Message message =
                Message.CreateMessage(channel.GetProperty<MessageVersion>(),
                 Action , ActionData);
            IAsyncResult resultChannel =
                channel.BeginSend(message, new AsyncCallback(OnSend), channel);
            if (resultChannel.CompletedSynchronously)
            {
                CompleteOnSend(resultChannel);
            }

            //Start listening for callbacks from the service
            ReceiveLoop(channel);
        }

        void OnSend(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteOnSend(result);
        }

        void CompleteOnSend(IAsyncResult result)
        {
            IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;
            channel.EndSend(result);
        }

        void ReceiveLoop(IDuplexSessionChannel channel)
        {
            // Start listening for callbacks.
            IAsyncResult result = channel.BeginReceive(new AsyncCallback(OnReceiveComplete), channel);
            if (result.CompletedSynchronously) CompleteReceive(result);
        }

        void OnReceiveComplete(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteReceive(result);
        }

        void CompleteReceive(IAsyncResult result)
        {
            //A callback was received so process data
            IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;

            try
            {
                Message receivedMessage = channel.EndReceive(result);

                // Show the service response in the UI.
                if (receivedMessage != null)
                {
                    string text = receivedMessage.GetBody<string>();
                    _UiThread.Post(Client.ProcessData, text);
                }

                ReceiveLoop(channel);
            }
            catch (CommunicationObjectFaultedException exp)
            {
                _UiThread.Post(delegate(object msg) { System.Windows.Browser.HtmlPage.Window.Alert(msg.ToString()); }, exp.Message);
            }
        }

        void OnCloseChannel(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
                return;
            else
                CompleteCloseChannel(result);
        }

        void CompleteCloseChannel(IAsyncResult result)
        {
            IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;
            channel.EndClose(result);
        }
    }
}

When the PushDataReceiver class’s Start() method is called by Silverlight it creates a channel factory instance which is used to create a channel instance.  The CompleteOpenChannel() callback method shown previously then sends an initial message to the service endpoint and encapsulates the data to be sent in a WCF Message object.  The message data is then sent along with the proper service action to call on the server.  After the initial message is sent a receive loop is started (see the ReceiveLoop() method) which listens for any messages sent from the server to the client and processes them accordingly.  Once a message is received the CompleteReceive() method is called and the message data is routed back to the Silverlight Page class.

Processing Data Using the XmlSerializer Class

The PushDataReceiver class shown earlier dispatches data received from the server back to the Silverlight Page class for processing.  Data sent from the server is in XML format and multiple techniques can be used to process it in Silverlight ranging from the XmlReader class to LINQ to XML functionality to the XmlSerializer class.  I chose to use the XmlSerializer class to process the data since it provides a simple way to map XML data to CLR types with a minimal amount of code.  Although you can create the CLR classes that XML data maps to by hand, I chose to create an XSD schema and use .NET’s xsd.exe tool to generate code from the schema for me.  The xsd.exe tool provides a simple way to generate C# or VB.NET code and ensures that the XML data will be successfully mapped to the appropriate CLR type’s properties.  An example of using the tool is shown next:

xsd.exe /c /namespace:SomeNamespace Teams.xsd

The /c switch tells the tool to generate classes (as opposed to strongly-typed DataSets) while the /namespace switch allows you to control what namespace is added into the auto-generated code.  Other switches are available which you can read more about here.

One of the XSD schemas used to generate C# code with xsd.exe is shown next:



  
    
      
        
          
            
              
                
                  
                  
                
              
            
            
          
        
      
    
  

Note: If you use the xsd.exe tool to generate classes that will be used in a Silverlight client you’ll have to remove a few lines that don’t compile from the auto-generated code.  The xsd.exe tool generates code designed to run on the full version of the .NET framework but with a few minor modifications you can also use the code with Silverlight.  Simply remove the namespaces and attributes that the compiler says are invalid from the auto-generated code.

Once data is received by the Silverlight client from the WCF polling duplex service it’s processed by a method named ProcessData() (the method called by the PushDataReceiver class) in the sample application.  ProcessData() uses the XmlSerializer class to deserialize XML data into custom Teams and ScoreData objects (the Teams and ScoreData classes were generated from XSD schemas using the xsd.exe tool mentioned earlier).

public void ProcessData(object receivedData)
{
    StringReader sr = null;
    try
    {
        string data = (string)receivedData;
        sr = new StringReader(data);
        //Get initial team data
        if (_Teams == null && data.Contains("Teams"))
        {
            XmlSerializer xs = new XmlSerializer(typeof(Teams));
            _Teams = (Teams)xs.Deserialize(sr);
            UpdateBoard();
        }

        //Get updated score data
        if (data.Contains("ScoreData"))
        {
            XmlSerializer xs = new XmlSerializer(typeof(ScoreData));
            ScoreData scoreData = (ScoreData)xs.Deserialize(sr);
            //ScoreDataHandler handler = new ScoreDataHandler(UpdateScoreData);
            //this.Dispatcher.BeginInvoke(handler, new object[] { scoreData });
            UpdateScoreData(scoreData);
        }
    }
    catch { }
    finally
    {
        if (sr != null) sr.Close();
    }
}

As team and score data is pushed from the server to the client it’s updated on the Silverlight interface as shown next:

The complete code for the application including the WCF duplex polling service and the Silverlight client can be downloaded here.

Twitter

Already on Twitter and interested in getting live updates about blog posts and other information?  Subscribe to my Twitter feed at www.twitter.com/DanWahlin.

Turn Firefox 3′s Location Bar Yellow at https:// URLs [Firefox 3]

yellowhttps.pngOne noticeable change between Firefox 2 and Firefox 3 is the yellow address bar background, which turned on in Firefox 2 when you visited encrypted web sites—the ones that start with https://. After much debate among the developers, Firefox 3 dropped that visual cue, but on Windows, with a little userChrome.css tweak, you can have that yellow background back. Here’s how.

If you’ve got the Stylish extension installed, you can simply add a new style that contains:

#urlbar[level] .autocomplete-textbox-container { background-color: #FFFFB7 !important; }

Otherwise, here’s how to manually edit your userChrome.css and add that code to it.

Note: this tweak does NOT work on the Mac, just Windows. (Not sure about Linux, feel free to let us know in the comments if you try it.) Also, there was long debate and solid reasoning behind the decision to “de-yellow” https:// sites, here’s more on that.

Another reader-submitted userChrome.css tweak dims the RSS icon and star in the location bar unless you hover over it—get that here.

We’re on the hunt for the best userChrome.css and Stylish tweaks for Firefox 3, since many of the ones we knew and loved in Firefox 2 no longer work. So fess up: what are your must-have user styles or userChrome.css additions for Firefox 3?

Disable Blinking Text in Firefox [Firefox Tip]


If blinking text on a web page (like in the comments here on Lifehacker) is threatening to give you a seizure—or just making your head hurt—you can disable it in Firefox with a simple configuration tweak. No extension or user script required: just type about:config in Firefox’s address bar, press the “I’ll be careful, I promise!” button, and then in the Filter field, enter browser.blink_allowed. Change the value for that key from true to false, and you’re good to go, no more blinking, ever again. Thanks, David!


Freeware PDF Unlocker Enables Copy/Paste Functions Without Passwords [Password Recovery]

pdf_unlocker.pngWindows only: Freeware PDF Unlocker, a free Windows utility, doesn’t do everything that commerical packages like those from Elcomsoft do, but it will help if you just need to unlock copy/paste and editing restrictions on a PDF. Simply drag a copy-protected PDF onto PDF Unlocker’s icon, and a new, non-protected copy will be created. You can also strip PDF files of passwords needed to open and read them, but you, uh, need the password to do so. For simple restrictions, however, it does the job. Freeware PDF Unlocker is a free download for Windows systems.


WP Like Button Plugin by Free WordPress Templates