# Thursday, June 14, 2007

Quick Tip - Input Controls in Silverlight

UPDATE 10/25/2007: Updated this sample to function correctly with the Silverlight 1.0 release. The main changes necessary were the typical 0.9 to 1.0 changes (remove "Sys.", isWindowless now takes a string and not a boolean, event handlers no longer string based), and I also had to explicitly set the z-index of the INPUT element. Thank you Erik for bringing it to my attention.

 

I have seen a lot of questions and confusion regarding capturing input when using Silverlight. There are no Button, TextBox, or other similar controls to work with. I see many folks asking for help with building their own directly within Silverlight - and bless their hearts, that is a daunting task indeed! The UI model currently does not offer basic input capture features such as "focus" or "tabbing", or even control-level keystroke capture. So folks tend to start building those basic services first, before they ever even get to writing code and xaml to support that simple textbox they need.

If you read that first paragraph and thought "thats crazy, it shouldn't be that hard!", then I would agree with you. Fortunately, there is a FAR easier and more robust way to achieve the same thing. In fact, its something that is not new to Silverlight at all, it's been with us for years. I am of course talking about the tried-and-true html <input> tag.

One of the most overlooked aspects of Silverlight is that it is a component, not a platform. Your browser is the platform. It can do a lot of stuff, if you just ask it to. Nobody wants an entire site as a single Silverlight canvas, just like nobody wants an entire site as a Flash canvas (unless possibly it is a mini-game or rich media application)... Flash designers realized this fact years ago. And as a component, a part of the solution if you will, Silverlight can play nicely with it's neighbors. With just a little bit of effort and sprinkling a very minimal amount of javascript pixie dust, we can get a Silverlight applet talking to the rest of our html DOM. And that's exactly what I am going to show in this topic...

You can download the code demonstrated in this article here (QuickTip-TextboxesInSilverlight.zip).

First of all, I am doing this with Silverlight 1.0 beta (the javascript one), as I think the 1.1 alpha is far too likely to change, and this technique should work with either. That, and I am lazy and don't want to come back and revisit this post later to correct the code...

Secondly, I am using the current CTP builds of Visual Studio Orcas and Blend 2 (the May 2007 bits), both with the Silverlight extensions. If you are using something else, then your mileage may vary.

UPDATE 10/25/2007: Code updated for VS2008 beta2 and the RTM version of Silverlight 1.0.

Now on to the code... to be sure we are on the same page, I am creating a new project from scratch...

First, Create a new project in Blend. Select the Silverlight 1.0 (JavaScript) project type. It does not matter what you name the project, but for this example I went with "TextboxesInSilverlight".

Switch to XAML view and replace the default canvas with this markup:

<Canvas
 xmlns="
http://schemas.microsoft.com/client/2007"
 xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml"
 Width="252" Height="272"
 Background="#FFFF2121"
 >
  <TextBlock Width="64" Height="24" Canvas.Left="8" Canvas.Top="8"
             Text="Opacity" TextWrapping="Wrap"/>
  <Ellipse Opacity="1" Fill="#FF0406FF" Stroke="#FF000000"
           x:Name="TheCircle" Width="180" Height="180" 
           Canvas.Left="36" Canvas.Top="64"/>
</Canvas>

This will create a simple red canvas, with a blue circle.

At this point, I generally switch over to Visual Studio Orcas since Blend does not have Intellisense nor does it really know how to deal with JavaScript. You can do this easily by right-clicking a project item (such as the xaml file) and selecting "Edit in Visual Studio".

Next, we need to do one small housekeeping chore to make sure that our Silverlight canvas plays nicely - specifically, we need to ask it to operate "windowless", which will allow other dhtml elements to overlay it, Open the Default.html.js file, and modify the call to Silverlight.createObjectEx(). We want to add the parameter for isWindowless...

 Silverlight.createObjectEx({
  source: "Scene.xaml",
  parentElement: document.getElementById("SilverlightControlHost"),
  id: "SilverlightControl",
  properties: {
   width: "100%",
   height: "100%",
   version: "0.9",
   isWindowless: "true"
  },
  events: {
   onLoad: sceneLoaded
  }
 });

To make things a bit more "clean", we will create the input element directly from code, however it's always good to control visual styling with CSS. Therefore, open up the Default.html, and alter the <style> tag to match the following:

 <style type="text/css">
  div, body, input
  {
   margin: 0;
   padding: 0;
  }
  #opacity
  {
   margin: -272px 0 0 75px;
   z-index: 100;
  }
  .silverlightHost
  {
   margin: 40px auto auto auto;
   height: 272px;
   width: 252px;
  }
 </style>

This will handle the placement and sizing of the silverlight container <div> as well as the input control itself. The negative margin is not a typo - this is used to pull the input control "on top" of the silverlight canvas. We could have also used absolute positioning, but that is much more brittle, relative positioning FTW. Also, notice the use of "auto" margins for the main Silverlight Host <div>. This is how you can center content without resorting to using <center> or <table>... if you take nothing away from this post, at least remember that one trick.

The last thing we must do is wire the whole thing up. This can be done in many places, in many ways. I consider this particular TextBox to be an extension of the Xaml "scene", so I will add my code to the TextboxesInSilverlight.Scene class which was created for us by the Blend project template. This is not the only place you could do this kind of code, but I found that in this particular example it made the most sense. Had I been building a dialog for a game engine, I might have this code in a seperate script file that manages my game mechanics (but thats another article...).

First, we need to capture a global reference to the Scene object that is created (this object is instantiated by the createSilverlight() function of the Default.html.js script file we edited in a previous step). The purpose of capturing this reference is that we will need it later in an event handler. This will allow our html <input> control to communicate back with the silverlight content. This is easier done than said. Open the Scene.xaml.js file. Just before the definition of the TextboxesInSilverlight.Scene.prototype (look up javascript prototype for what this is if you are interested, but that discussion is out of scope for this article), add a line of code to declare the global reference:

var globalScene = null;

Now we will create a callback function that will be used to create our JavaScript object and initialize it:

function sceneLoaded(control, userContext, rootElement)
{
    globalScene = new TextboxesInSilverlight.Scene();
    globalScene.handleLoad(control, userContext, rootElement);
};

Now, the idea for this example is that the value of the text box will determine the "Opacity" Xaml property of the Ellipse shape in our markup. In order for the event handler we are about to add to be able to do this, we will capture a reference to the circle object (technically we can wait and use findName() later during the event handler, but I prefer to capture it only once - its just my style of coding). Add this line to the handleLoad function:

this.circle = control.content.findName("TheCircle");

Next, we will create the input control and add it to the DHTML document. We will add it directly to the same <div> that Silverlight has injected itself into, and therefore any layout or positioning that affects the Silverlight canvas will also affect our <input> box. Add this code to the end of the handleLoad function:

var opacityEdit = window.document.createElement("input");
opacityEdit.type = "text";
opacityEdit.id = "opacity";
opacityEdit.name = "opacity";
opacityEdit.value = "1.0"
;

We are almost done - only two more steps and then we can fire this thing up! First, we need to add an event handler to react to changes in the value property of the input control. Add this code to the very end of the handleLoad function:

opacityEdit.onpropertychange = function()
    {
        if (event.propertyName == "value")
        {
            globalScene.circle.Opacity = event.srcElement.value;
        }
    }

This effectively creates an anonymous function to handle property change events on the <input> control, which in turn updates the Opacity property of the circle shape. Cool, huh?

The last thing to do is finally add the new <input> element to the page, otherwise all the work until this point will have had no discernable impact at all... add this one last line to the handleLoad function:

this.control.parentElement.appendChild(opacityEdit);

At this point, your Scene.xaml.js file should look like this:

if (!window.TextboxesInSilverlight)
 window.TextboxesInSilverlight = {};

TextboxesInSilverlight.Scene = function()
{
}

var globalScene = null;

function sceneLoaded(control, userContext, rootElement)
{
    globalScene = new TextboxesInSilverlight.Scene();
    globalScene.handleLoad(control, userContext, rootElement);
}

TextboxesInSilverlight.Scene.prototype =
{
 handleLoad: function(control, userContext, rootElement)
 {
  this.control = control;
  
  this.circle = control.content.findName("TheCircle");
  
  var opacityEdit = window.document.createElement("input");
  opacityEdit.type = "text";
  opacityEdit.id = "opacity";
  opacityEdit.name = "opacity";
  opacityEdit.value = "1.0";
  opacityEdit.onpropertychange = function()
    {
      if (event.propertyName == "value")
      {
        globalScene.circle.Opacity = event.srcElement.value;
      }
    }
  
this.control.parentElement.appendChild(opacityEdit);
 } 
}

If the typo gods favor you, then you should be able to use F5 to run the page and see it all working together:

Notice the positioning of the input textboxes, and the interaction of them with the underlying Silverlight canvas. This is just a simple example, but can be a very powerful way to "plug in" Silverlight into the surrounding DHTML.

Thursday, June 14, 2007 4:25:05 PM (Eastern Daylight Time, UTC-04:00) #  Disclaimer | Comments [0] | 

# Wednesday, May 23, 2007

Understanding what it means to "hit the curve"

The past few months I have been diving deeper and deeper into the two main pillars of .NET 3.0 - WPF and WCF. This post by Karsten Januszewski, although a year old, totally resonates even today.

I always laugh a little (on the inside) when I hear "senior" developers claim how all the difficult "real" work is to be found in back-end services and components. They assert that all UI development is easy, and doesn't even require much developer skill. Nothing could be further from the truth, and the new UI framwork (WPF) makes this glaringly obvious.

Basic concepts can be difficult to grasp like the layout system - where controls get to "vote" on their size and position, but it is really up to their container to make the decision. Another one that is hard to get a handle on - control templates and data templates... very powerful indeed, but a completely new approach to build an interface. Or just the whole concept of resources and style resolution. It feels a little bit like CSS... until you realize that nothing really cascades, it only overrides.

Now, don't misinterpret this as a bash towards WPF... I really feel the complexity and learning curve are warranted, and that the platform is above and beyond anything that was available before. But XAML is HARD until you get the swing of it, and don't let anyone try to convince you otherwise.

.NET | WPF
Wednesday, May 23, 2007 1:56:33 PM (Eastern Daylight Time, UTC-04:00) #  Disclaimer | Comments [2] | 

# Thursday, May 10, 2007

Atlanta Geek Dinner

The Geek Dinners are back. Although usually attended by microsoft developer types, these are really open to anyone with a geeky side that needs reassurances from their peers that "it's OK to be a geek".

Shawn has more details here. Be sure to RSVP with him if you are going - so tha he can handle the accomodations properly.

Thursday, May 10, 2007 3:07:35 PM (Eastern Daylight Time, UTC-04:00) #  Disclaimer | Comments [0] | 

# Sunday, May 06, 2007

Atlanta Cutting Edge .NET - May 2007

Tomorrow night is the Atlanta Cutting Edge .NET User Group meeting for May. Actually, it's also the May meeting for the Atlanta MS Pros and VB.NET groups as well. We will all be sharing the facility and sponsorship, as well as having a combined networking period before the meeting and a combined "keynote" presentation for the first half hour. After that, we will split up into 3 rooms (well technically, we will probably just close the walls and divide the main room into individual sections), each of which will offer a seperate track of material. We have a track for Cutting Edge, one for VB.NET, and one for MS Pros.

For more details, including the topics and speakers that are scheduled, please refer to the user group website at http://www.atlantace.com/

.NET | Events | General
Sunday, May 06, 2007 8:30:37 PM (Eastern Daylight Time, UTC-04:00) #  Disclaimer | Comments [0] | 

# Monday, April 30, 2007

A storm is brewing

It has been a good while since this industry has had a significant shake-up, where the world as we know it changes almost overnight, and our skills and practices are all made obsolete. We generally seem to have one every few years or so, and according to my calculations, it has been about 7 or 8 years since the last one.

I am sure there were other events, but the first I can recall was the introduction of Object Oriented programming in the 80's. This paradigm shift left multitudes of mainframe COBOL and RPG analysts behind, forever to toil in a world of green on black terminal displays. Then in the early and mid 90's there was an explosion of "client-server" and "N-Tier" applications in the business world. These were all the rage, and again the flock was divided. Many OOP purists were left in the dust, trying to fend off the "younger kids" that embraced the 3GL and 4GL tools of the day. But as luck would have it, only a few years later the terms "client-server" and "N-Tier" took a back seat to the newest technology explosion - the age of the Web. Right or Wrong, everyone wanted to be on the web. Try as they might, the n-tier supporters could not withstand this assault. To this day, there remains a contingent of developers that cling to the world before the web - in the Microsoft kingdom, we call them "Windows Forms Programmers", or perhaps the slightly more dignified "Smart Client Developers". But the significant majority of development work admittedly goes into Web applications.

So for the last few generations of the industry, roughly every five to seven years, we experienced a wholesale disruption in the status quo. Things are no longer what we thought they were. Skills become unmarketable. Management becomes confused. Projects get scrapped. We have to retool - retool or else go the way of the Do-Do Bird (extinct).

The only problem is - it has been about eight years now since the last paradigm shift (I do not count .NET as a paradigm shift - it is simply a consolidation and improvement on ideas and methods already in place). It has been eight years, and I fear that we are long overdue. More than that though - I feel that perhaps, just perhaps, the paradigm shift has already begun - and that I can't see it due to my own Myopia. And what if the shift has already passed me by, and I have missed it entirely?

In conclusion, I think the shift is just now underway. I have smelled the crispness in the air that precedes a thunderstorm. I think the industry is about to change again, in a very significant way, and I hope to be a small part of it yet again. But in order to accept and participate in a significant change, a person must adapt to the new way of things. To that end I have begun the arduous task of retooling and rebranding myself. This will not be the first time, nor likely the last. As a as/400 specialist, converted to PC technician, converted to Delphi developer, converted to DBA, converted to Web Developer, and finally to .NET windows/web developer, I can say that I have definately been through this process before, and it does not scare me. What scares me is the thought of not adapting.

Some of the people I know and trust feel that they too have "seen the light". Some have their own theories about where to be when the music stops playing. My good friend Scooter seems to think that Sharepoint is the entire future. I don't necessarily agree with that. I have heard similar theories about the grand direction of things from others as well ("Linux is the future!", "Everything will be AJAX!", "OMGZ It's all going to Pocket PC format!") - most of which I cannot find reason with either. Everyone seems to agree that the winds are changing, only nobody appears to agree on the direction. But I have my own ideas and theories and will once again be betting the next half-decade or longer of my career on that insight. It hasn't let me down in the past - I trust it will not let me down this time either.

.NET | General | Silverlight | Web 2.0 | WPF
Monday, April 30, 2007 4:29:00 PM (Eastern Daylight Time, UTC-04:00) #  Disclaimer | Comments [2] | 
View Keith Rome's profile on LinkedIn

On this page....

Archives

Navigation

Categories

About

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

Sign In

Certification Logo Certification Logo Certification Logo Certification Logo Certification Logo

Powered by: newtelligence dasBlog 2.3.9074.18820