Silverlight Ux Musings: Controlling Alignment of UI Elements [Corrina Barber]

Silverlight Ux Musings: Controlling Alignment of UI Elements [Corrina Barber]

  • Comments 4

This is my first post on the VB blog! I’m a designer on the team with a background in visual and interaction design, and I also have some .NET coding experience. I plan to start blogging on the VB blog on a regular basis, and my goal is to share some of my thoughts on and explorations with Silverlight.

I’m sych'd about the possibilities enabled by Silverlight for many reasons, but, being a designer, one of the main reasons is that I can actually get cool UI implemented exactly as I envisioned. I love that I can create unique and beautiful UI in Blend, bring it into Visual Studio, and then add the interactivity and business logic I desire using VB.

So anyway, one of the first things I did when I began my Silverlight explorations was to layout my XAML UI where some elements aligned left and some elements aligned right, and I wanted to make sure my UI remained exactly as I had planned even if the browser was at a size I hadn’t planned for. I found that Silverlight 1.1 didn’t have a container control to help me do this (at least not yet), so I quickly coded up a solution to this problem, and that’s what I’m going to share today.

Just one additional blurb before we dive into things, this solution targets a specific problem, but it can be used in many other ways. The gist of the solution enables sharing of events and data between VB and JavaScript code, so with a little imagination you’ll be able to envision many other uses of the concepts presented in this post. In fact the Silverlight.NET site offers information on this same concept, in case you would like to explore things further.

Solution Screen Shots

The images below show how the little yellow buttons that are aligned right remain properly aligned right even as the browser is resized…

Alignment of Ux on resize in Silverlight

 

 

 

 

 

 

 

 

Necessities

Specific details can be found on the Silverlight.NET site

§  Microsoft Silverlight 1.1 Alpha September Refresh

§  Visual Studio 2008 Beta2

§  Microsoft Silverlight Tools Alpha Refresh for Visual Studio 2008 Beta 2 (July 2007)

Details

The following steps detail out how to implement the UI and behavior pictured in the images above…

The first step is to create a new VB Silverlight project

0.     Create a new VB Silverlight project by selecting File -> New -> Project…, and then selecting Visual Basic in the Project types pane and Silverlight Project in the Templates pane

The next step involves tweaking JavaScript a bit. To handle repositioning of elements, we first need to get a reference to the Silverlight control, so we can get its size. We then need to send the size information back to the .NET code. What follows are the changes that are required in the JavaScript code to enable this functionality

1.     Open TestPage.html.js by selecting File -> Open -> File…, and then opening TestPage.html.js from your newly created Silverlight Project directory

2.     Modify TestPage.html.js as follows (the code in gray is the original code, and the red underlined code identifies what you need to modify in the file while the full color code is what you need to add to the file)

// JScript source code

 

//contains calls to silverlight.js, example below loads Page.xaml

function createSilverlight()

{

  Silverlight.createObjectEx({

         source: "Page.xaml",

         parentElement: document.getElementById("SilverlightControlHost"),

         id: "SilverlightControl",

         properties: {

                width: "100%",

                height: "100%",

                version: "1.1",

isWindowless: "true",

                background: "#00000000",

                enableHtmlAccess: "true"

              },

             

events: { onLoad: OnLoaded }

       });

         

       // Give keyboard focus to the Silverlight control by default

  function OnLoaded(sender, args) {

      var silverlightControl = document.getElementById('SilverlightControl');

      if (silverlightControl)

      silverlightControl.focus();

     

      // Onload call the method that sends the SL control size to the .NET code

      CallScriptable();

    }

        

  // Send the SL control size to the .NET code

    function CallScriptable() {

      // Get the SL control instance

      var silverlightControl = document.getElementById('SilverlightControl');

       

      // Get the SL control width and height

    var slWidth = silverlightControl.offsetWidth;

    var slHeight = silverlightControl.offsetHeight;

   

      // Call the .NET code with the SL control size passed over

    silverlightControl.Content.DotNetToJS.getSLSize(slWidth, slHeight);

    }

}

 

3.     Open TestPage.html from the Solution Explorer

4.     Modify TestPage.html as follows (items in gray are original, and items in red and underlined represent items that need modification while the full color items represent items you need to add to the file)

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

<html xmlns="http://www.w3.org/1999/xhtml" >

<!-- saved from url=(0014)about:internet -->

<head>

    <title>Silverlight Project Test Page </title>

   

    <script type="text/javascript" src="Silverlight.js"></script>

    <script type="text/javascript" src="TestPage.html.js"></script>

   

    <script type="text/javascript">

    // Call CallScriptable on window resize

    window.onresize = CallScriptable;

       

  // Send the SL control size to the .NET code

    function CallScriptable()

    {

        // Get the SL control instance

        var silverlightControl = document.getElementById('SilverlightControl');

       

        // Get the SL control width and height

      var slWidth = silverlightControl.offsetWidth;

      var slHeight = silverlightControl.offsetHeight;

   

        // Call the .NET code with the SL control size passed over

      silverlightControl.Content.DotNetToJS.getSLSize(slWidth, slHeight);

    }

    </script>

   

    <style type="text/css">

        .body            { margin: 0px;

                           background-color: #FF000000; }

                      

        .silverlightHost { position: absolute;

                           z-index: 0;

                           width: 100%;

                           height: 100%;

                           background-color: Transparent; }

    </style>

</head>

 

<body class="body">

    <div id="SilverlightControlHost" class="silverlightHost">

        <script type="text/javascript">

            createSilverlight();

        </script>

    </div>

</body>

</html>

 

The next step involves adding the appropriate VB code to your project (in all of the following, gray code will indicate the original code and full color code will indicate what you need to add to the code file)

5.     Add an import for System.Windows.Browser, so we can provide access to .NET scriptable elements

6.     Mark the partial Page class as scriptable by adding <ScriptableI()> _ above the partial Page class

7.     Add a scriptable public event handler as shown below

8.     Register the class as a scriptable object in the method where initialization is handled. In this case, this is the Page_Loaded method (this method is specified as the one to handle onload or initialization functionality in the default or parent Canvas provided in Page.XAML – Loaded = “Page_Loaded”)

'Import that needs to be added

Imports System.Windows.Browser

 

' XAML page class

<Scriptable()> _

Partial Public Class Page

    Inherits Canvas

 

    ' Event for talking between .NET and Javascript

    <Scriptable()> _

    Public Event JSToDotNetEvent As EventHandler(Of CustomEventArgs)

 

    Public Sub Page_Loaded(ByVal sender As Object, ByVal e As EventArgs)

 

        'Register the scriptable endpoint - String is what's referenced in script

        '“silverlightControl.Content.DotNetToJS.getSLSize(slWidth, slHeight);” in

        'TestPage.html.js

        WebApplication.Current.RegisterScriptableObject("DotNetToJS", Me)

 

        ' Required to initialize variables

        InitializeComponent()

 

    End Sub

 

End Class

 

9.     Add the scriptable method that is to be called from the JavaScript. In this case, we called getSLSize() from the JavaScript. This method should raise the event marked scriptable, and then it can call the method where we handle positioning of XAML elements on the page

10. Add the method that handles positioning of XAML elements on the page. This method should take the Silverlight width and height data, and then apply appropriate positioning specifications to the XAML elements. In this case, we’re going to position the Canvas.Left attribute of the “MainBtnsGroup” canvas using the SetValue method as shown below

' Method that is called by JS to pass in Silverlight size information

     <Scriptable()> _

     Public Sub getSLSize(ByVal numWidth As Integer, ByVal numHeight As Integer)

 

        ' Raise event

        RaiseEvent JSToDotNetEvent(Me, New CustomEventArgs(numWidth, numHeight))

 

        'Call method to position XAML elements

        PositionXAML(numWidth, numHeight)

 

     End Sub

 

     ' Position XAML elements

     Private Sub PositionXAML(ByVal numWidth As Integer, ByVal numHeight As Integer)

 

        'Define appropriate values for aligning the desired elements

        Dim numMainButtonsGroupWidth As Integer = numWidth - 25

 

        'Position elements

        MainBtnsGroup.SetValue(Canvas.LeftProperty, numMainButtonsGroupWidth)

 

     End Sub

 

11. Create a new CustomEventArgs class that inherits from EventArgs. This is used by our scriptable event and it allows us to pass simple data between JavaScript and .NET code. In this case, we’re simply passing Integer values for the width and height of the Silverlight control

' Class to hold custom event arg parameters

<Scriptable()> _

Public Class CustomEventArgs

    Inherits EventArgs

 

    ' Private variables

    Private _numWidth As Integer

    Private _numHeight As Integer

 

    ' Constructor

    Public Sub New(ByVal inumWidth As Integer, ByVal inumHeight As Integer)

        _numWidth = inumWidth

        _numHeight = inumHeight

    End Sub

 

    ' Getter and Setter for width

    <Scriptable()> _

    Public Property NumWidth() As Integer

        Get

            Return _numWidth

        End Get

        Set(ByVal value As Integer)

            _numWidth = value

        End Set

    End Property

 

    ' Getter and Setter for height

    <Scriptable()> _

    Public Property NumHeight() As Integer

        Get

            Return _numHeight

        End Get

        Set(ByVal value As Integer)

            _numHeight = value

        End Set

    End Property

 

End Class

 

12. The final, bit involves adding the XAML. The following XAML is what I used in the example. Simply add it inside of the default canvas that is provided when you first create your project. I also recommend deleting the height, width, and background attributes of the default canvas to ensure your UI will look exactly like the screen shot above

  <!-- TOP WHITE SECTION -->

  <Canvas x:Name="NavSection_TopWhiteBars" Width="5000" Canvas.Top="1" Canvas.ZIndex="0">

    <Rectangle Fill="#FFFFFF" Height="5" Width="66" Canvas.Left="1"></Rectangle>

    <Rectangle Fill="#FFFFFF" Height="5" Width="169" Canvas.Left="68"></Rectangle>

    <Rectangle Fill="#FFFFFF" Height="5" Width="80" Canvas.Left="238"></Rectangle>

    <Rectangle Fill="#FFFFFF" Height="5" Width="80" Canvas.Left="319"></Rectangle>

    <Rectangle x:Name="TopWhiteBar_Right" Fill="#FFFFFF" Height="5" Width="5000" Canvas.Left="400"></Rectangle>

  </Canvas>

  <!-- TOP WHITE SECTION -->

 

  <!-- MAIN BUTTONS -->

  <Canvas x:Name="MainBtnsGroup" Canvas.Top="20">

    <!-- MAXIMIZE -->

    <Canvas x:Name="MainBtn_Maximize" Height="11.458" Width="11.875" Background="Black">

      <Path Width="11.875" Height="11.449" Stretch="Fill" Stroke="#FFFFE513" Canvas.Left="0" Canvas.Top="0" Data="M394.125,212.125 L408.375,212.125 408.375,226.125 394.125,226.125 z" StrokeThickness="1.7"/>

      <Path Width="5.862" Height="5.478" Stretch="Fill" Stroke="#FFFFE513" Canvas.Left="3" Canvas.Top="3" Data="M394.125,212.125 L408.375,212.125 408.375,226.125 394.125,226.125 z" StrokeThickness="1"/>

      <Path Width="6.25" Height="1" Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FFFFE513" StrokeThickness="1" Canvas.Left="3.038" Canvas.Top="3.5" Data="M397.75,215.875 L403,215.875"/>

    </Canvas>

    <!-- MAXIMIZE -->

    <!-- LOCAL -->

    <Canvas x:Name="MainBtn_Local" Height="11.458" Width="11.875" Background="Black" Canvas.Top="20">

      <Path Width="11.875" Height="11.449" Stretch="Fill" Stroke="#FFFFE513" Canvas.Left="0" Canvas.Top="0" Data="M394.125,212.125 L408.375,212.125 408.375,226.125 394.125,226.125 z" StrokeThickness="1.7"/>

      <Path Width="5.966" Height="6.309" Fill="#00FFFFFF" Stretch="Fill" Stroke="#FFFFE513" StrokeThickness="1.3" Canvas.Left="2.955" Canvas.Top="2.625" Data="M7.9164731,6.3749918 C7.9161396,8.8327213 4.8748622,8.999218 3.7500222,8.0416683 2.8935618,7.3125838 2.6777925,5.9521834 3.333701,5.0008178 4.1669285,3.7922593 5.8750436,4.0002844 5.8750436,4.0002844 L5.4547556,3.28128"/>

      <Path Width="2.5" Height="2.709" Fill="#00FFFFFF" Stretch="Fill" Stroke="#FFFFE513" StrokeThickness="1.5" Canvas.Left="5.54" Canvas.Top="2.751" Data="M6.3321424,42.872677 L6.1648931,44.580995 7.6641389,43.916021 z"/>

    </Canvas>

    <!-- LOCAL -->

    <!-- REFRESH -->

    <Canvas x:Name="MainBtn_Refresh" Height="11.458" Width="11.875" Background="Black" Canvas.Top="40">

      <Path Width="11.875" Height="11.449" Stretch="Fill" Stroke="#FFFFE513" Canvas.Left="0" Canvas.Top="0" Data="M394.125,212.125 L408.375,212.125 408.375,226.125 394.125,226.125 z" StrokeThickness="1.7"/>

      <Path Width="5.966" Height="6.309" Fill="#00FFFFFF" Stretch="Fill" Stroke="#FFFFE513" StrokeThickness="1.3" Canvas.Left="2.955" Canvas.Top="2.625" Data="M7.9164731,6.3749918 C7.9161396,8.8327213 4.8748622,8.999218 3.7500222,8.0416683 2.8935618,7.3125838 2.6777925,5.9521834 3.333701,5.0008178 4.1669285,3.7922593 5.8750436,4.0002844 5.8750436,4.0002844 L5.4547556,3.28128"/>

      <Path Width="2.5" Height="2.709" Fill="#00FFFFFF" Stretch="Fill" Stroke="#FFFFE513" StrokeThickness="1.5" Canvas.Left="5.54" Canvas.Top="2.751" Data="M6.3321424,42.872677 L6.1648931,44.580995 7.6641389,43.916021 z"/>

    </Canvas>

    <!-- REFRESH -->

  </Canvas>

<!-- MAIN BUTTONS -->

Ending Comments

I hope you found this post interesting and/or useful  J

Leave a Comment
  • Please add 1 and 1 and type the answer here:
  • Post
  • Great article ... let's see more SilverLight 1.1 examples with VB.

    bill

  • Hi Corrina,

    Great post! My VB is a bit rusty, I am a rather hardcore C# programmer, though I did a rather long project in VB (not .NET…) some time ago and I do quite a lot of automation in VBA for my Office documents… feel my pain 

    I like very much that your post gives a concrete example of JavaScript - .NET interaction. I am convinced that this is one of the greatest features of Silverlight. I will definitely write about this when the time comes and your post will be helpful.

    After reading, I realized how neat the Grid, DockPanel, etc… are… After a time using WPF, you tend to forget how limited the Canvas layout is. Auto layout is really a great achievement of WPF over WinForms. Can’t wait to see more panels (and controls) coming to Silverlight.

    Thanks,

    Laurent

  • >> I’m sych'd

    What is "sych'd", exactly ?

    Is the "p" invisible as well as silent ?

  • Oops! Look at that, I forgot the 'p' ;)

Page 1 of 1 (4 items)