Taoffi's blog

prisonniers du temps

Silverlight database to DataGrid, yet another approach- Part V

The data record (the ‘form’)

As I mentioned in Part II, my proposed solution (aimed to transfer and communicate data between a Silverlight Client and the database server) is composed of the following schematic classes:

Data level classes

SilverDataTable

Represents the table (or view, or function…) data.

Contains:

A meta-data table

A list of data rows

SilverDataRow

Represents one record of data.

Contains:

List of data cells

Linked to:

A parent table

SilverDataCell

Represents one record’s data cell.

Linked to:

A parent row

A parent meta-data column

Meta-data level classes

SilverMetaTable

Represents the table’s meta-data (schema)

Contains:

A list of meta-data columns

Linked to:

A parent table

SilverMetaColumn

Represents one column’s schema information.

Note: This is the ‘Key Class’ where you can insert all your required business logic.

Linked to:

A parent meta-data table

 

The SilverMetaColumn class is the key class which will transport many of business logic information from the database server to the client (and back to the server).

A meta-data column object of this class can transport information like:

§  The column’s data type;

§  Can the column contain null value?

§  The semantic type of the field (ex: control type… i.e. is this an option list? a value entry field?…)

§  Valid value ranges;

§ 

§  etc.

 

What I want to expose here is: how to use this meta-data column object to ‘automatically’ (or semi-automatically) compose a data record (form) on the client side.

 

From the field semantic àto the control àto the form

After having examined many of the data entry forms in various contexts, I think that a data entry (from the user interface point of view) is either:

§  A value entry control (i.e. a TextBox or TextBox-like: Numeric Up-down control for example)

§  Or a choice control (i.e. a ListBox, ComboBox, CheckBox, Radio-buttons-collection or other type of collection container controls)

 

Of course, the visual aspect of the control and its interactivity scenario may vary… but its presentation-semantic remains the same: a value entry or a choice.

From the business logic point of view (at the server side), what is important is not the visual aspect or visual transitions for presenting and collecting the data. What is important is the conformance of the collected data to specific business logic constraints:

§  Does the entry conform to the required data type?

§  Does the entry conform to the required valid range?

§ 

 

According to this approach, we can imagine a solution where the server tells the client:

§  Here is the requested data record composed of:

§  Column 1 : this is the record key column, It is Read-only (i.e. it will be handled at the server)

§  Column 2: this is an entry of data type Text, Required (cannot be null), cannot exceed 50 characters, Current value is “sample entry”;

§  Column 3: this is an entry of data type integer, Can be null, Valid range is 0 through 40, Default is 25, Current value is 32;

§  Column 4: this a single choice of the following option list (x1, x2, x3), Cannot be null, Current value is x2;

§  Column 5: this a boolean value, default is True, Current value is False;

§ 

 

Receiving this information, the client can then undertake the presentation process, for example:

§  Start record presentation transition (animation);

§  For each column, create the corresponding control (ex: according to the user interface graphical chart);

§  Set each control’s properties to conform to business constraints (read-only, max chars, valid values check… etc.)

 

Here is an example of a solution using this approach:

 

Html Content Viewer for Silverlight

In a previous post, I talked about a solution to manipulate (animate for example) the Silverlight control inside the hosting html page.

 

The reverse side of the problem (displaying html content inside a Silverlight control), is a requirement which comes out in the context of numerous web projects.

 

Some of the proposed solutions suggest html translators in order to obtain the final text into a sort of ‘Rich Edit’ control.

Although it is very good to have a rich edit Silverlight control, the solution seems too tedious to solve a relatively simple problem: displaying html content into an html page (i.e. the page hosting our Silverlight control).

 

Building on the previous sample of animating the Silverlight control into the hosting page, I tried to make a user control that dynamically creates an iFrame to display the desired html content inside the main Silverlight page.

 

You can have a look at the proof of concept Here!

 

 

 

Dynamically create an html control

Suppose we want to display the html page: http://mycompany.com/page.html inside our Silverlight control.

 

The solution can be:

§  Use the HtmlPage to locate the hosting HtmlElement (where our Silverlight control lives: that can be straightforward:  HtmlPage.Plugin.Parent;)

§  Create a new html container (for example, a DIV)

§  Place an iframe html element inside this container

§  Set the iframe source to the desired page

§  Insert the container into the hosting HtmlElement

 

 

Here is a sample code illustrating these steps

 

private void CreateHtmlContent()

{

       HtmlElement  plugin_div   = HtmlPage.Plugin.Parent,

                    new_div      = HtmlPage.Document.CreateElement("div"),

                    iframe       = HtmlPage.Document.CreateElement("iframe");

 

       /// set the new div position to absolute

       new_div.SetStyleAttribute("position", "absolute");

       new_div.SetStyleAttribute("z-order", "5");

 

       new_div.SetStyleAttribute("left", "200px");

       new_div.SetStyleAttribute("top", "40px");

       new_div.SetStyleAttribute("width", "400px");

       new_div.SetStyleAttribute("height", "400px");

 

       /// setup the iframe style, attributes and target page

       iframe.SetStyleAttribute("width", "100%");

       iframe.SetStyleAttribute("height", "100%");

       iframe.SetAttribute("src", "http://www.isosoft.org/taoffi/");

 

       /// add the iframe to the new div

       new_div.AppendChild(iframe);

 

       /// add the new div to the hosting html page

       plugin_div.AppendChild(new_div);

}

 

The solution exposed here can be a suitable foundation to solve many situations where html content can be composed and/or displayed inline within a Silverlight control.

Let us begin by making a custom control, a HtmlContentViewer say!

 

The sample code exposes some more!

 

SilverHtmlContent.zip (49.34 kb)

Animating Silverlight control inside the HTML Page

Live demo

.

 

 

A Silverlight control can communicate and interact with other HTML elements of the page on which it is placed.

This is done by using the HtmlPage object (namespace System.Windows.Browser)

 

By using HtmlPage, you can have access to all HTML elements of the current page and be able to get and set many of their properties and attributes, and, accessorily, call DHTML script functions that may be located there.

This means, among other things, that you can get and set style attributes of the HTML element inside which your own Silverlight control is hosted (the DIV element having the id commonly named "silverlightControlHost")

 

<div id="silverlightControlHost"

       ...

 

This gave me an idea about creating a custom control which would represent this same hosting html DIV element and, through this custom control, interact with the hosting element properties (width, height, location… etc.)

One of the nice features of Silverlight is the Storyboard animations usually used to animate Silverlight controls.

So, what if we try to use a storyboard to animate the Silverlight hosting html element!

 

Suppose that our animation should move the html host element across the page. From an html viewpoint, this would mean to change its top/left coordinates.

A Silverlight user control doesn’t contain Top / Left properties required to do such a job. So we have to add those properties to our custom control.

 

I first created an (empty) custom user control:

 

Xaml:

<UserControl x:Class="SilverHtmlInteraction.HostingDivControl"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

       Background="Transparent"

       SizeChanged="UserControl_SizeChanged"

    Width="200" Height="200">

    <Grid x:Name="LayoutRoot" Background="White">

 

    </Grid>

</UserControl>

 

The control members:

 

private HtmlElement m_div        = null;

 

public HtmlElement Div

{

       get { return m_div; }

       set { m_div = value; }

}

 

The new Left and Top properties

 

public double Left

{

       get { return attrib = GetDimension("left", "offsetLeft", 0.0); }

       set { SetDimentionAttribute("left", value); }

}

public double Top

{

       get { return attrib = GetDimension("top", "offsetTop", 0.0); }

       set { SetDimentionAttribute("top", value); }

}

 

 

How to obtain html element dimensions (width, height, left, top... etc.)

 

// get the html element’s dimension (example: width, height, left...)

// first try to find the dimension in the element’s style (width, height, left, top...)

// if not found, try to obtain the element’s dimension property (offsetWidth, offsetHeight...)

private double GetDimension(string attrib_name, string property_name, double default_value)

{

       double attrib = GetDimensionAttribute(attrib_name, default_value);

 

       // if the style attribute is not present, try to get the dimension property

       if (attrib <= 0.0)

             return GetDimensionProperty( property_name, default_value);

       return attrib;

}

 

// get the html element’s style attribute (example: width, height, left...)

protected double GetDimensionAttribute(string attrib_name, double default_value)

{

       if (m_div == null)

       {

             debug_display_div_error();

             return default_value;

       }

 

       // try to get the style attribute’s value

       string str_value = m_div.GetStyleAttribute(attrib_name);

 

       if (string.IsNullOrEmpty(str_value))

       {

             return default_value;

       }

       // remove the dimension's unit (px)

       str_value = str_value.Replace("px", "");

 

       double value = default_value;

 

       // try to convert to numeric value

       if (double.TryParse(str_value, out value) == false)

             return default_value;

       return value;

}

 

// get the html element’s dimension property (example: offsetHeight, offsetWidth...)

protected double GetDimensionProperty(string property_name, double default_value)

{

       if( m_div == null)

       {

             debug_display_div_error();

             return default_value;

       }

      

       string str_value    = m_div.GetProperty( property_name).ToString();

 

       if (string.IsNullOrEmpty(str_value))

       {

             return default_value;

       }

       double value = default_value;

 

       // try to convert to numeric value

       if (double.TryParse(str_value, out value) == false)

             return default_value;

 

       return value;

}

 

I then registered the new Left and Top properties with the dependency system:

 

public static DependencyProperty LeftProperty = DependencyProperty.Register(

                    "Left", typeof(double), typeof(HostingDivControl),

                    new PropertyMetadata(

                           new PropertyChangedCallback(LeftPropertyChanged) ));

 

public static DependencyProperty TopProperty = DependencyProperty.Register(

                    "Top", typeof(double), typeof(HostingDivControl),

                    new PropertyMetadata(

                           new PropertyChangedCallback(TopPropertyChanged)));

 

private static void LeftPropertyChanged(DependencyObject obj,

                           DependencyPropertyChangedEventArgs e)

{

       if (obj == null)

             return;

       if (obj is HostingDivControl != true)

             return;

 

       HostingDivControl ctrl = (HostingDivControl)obj;

 

       ctrl.Left    = (double)e.NewValue;

}

 

 

private static void TopPropertyChanged(DependencyObject obj,

                           DependencyPropertyChangedEventArgs e)

{

       if( obj == null)

             return;

       if( obj is HostingDivControl !=true)

             return;

 

       HostingDivControl   ctrl   = (HostingDivControl) obj;

 

       ctrl.Top     = (double) e.NewValue;

}

 

 

The html element accessor should do some work each time this member is changed:

 

public HtmlElement Div

{

       get { return m_div; }

       set

       {

             m_div = value;

 

             if (m_div != null)

             {

                    base.Width   = this.Width;

                    base.Height  = this.Height;

                    Left         = GetDimension("left", "offsetLeft", 0.0);

                    Top          = GetDimension("top", "offsetTop", 0.0);

 

                    // to be able to change the left and top propertites,

                    // the html element should have its position style set

                    // to relative or absolute.

                    If (string.IsNullOrEmpty( m_div.GetStyleAttribute("position")))

                           m_div.SetStyleAttribute("position", "relative");

             }

             else

                    m_div_id = "";

       }

}

 

 

We are now ready to use our new custom control to animate the Silverlight control inside its hosting html page.

 

<Grid x:Name="LayoutRoot" Background="Transparent">

       <local:HostingDivControl x:Name="host_div" Margin="0"/>

 

The animation Storyboard

Let’s create a storyboard that changes the top/left properties of our control (which is the silver light control itself)

Note: I couldn’t use Expression Blend to create the storyboard. Blend seemed confused about the properties to be animated for this ‘special’ control. So I ended up by creating the storyboard by hand!

 

<UserControl.Resources>

       <Storyboard x:Name="Storyboard1">

             <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"

             Storyboard.TargetName="host_div" Storyboard.TargetProperty="(Top)">

                    <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

                    <EasingDoubleKeyFrame KeyTime="00:00:00.50" Value="50"/>

                    <EasingDoubleKeyFrame KeyTime="00:00:00.80" Value="85"/>

                    <EasingDoubleKeyFrame KeyTime="00:00:01.00" Value="130"/>

             </DoubleAnimationUsingKeyFrames>

 

             <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"

             Storyboard.TargetName="host_div" Storyboard.TargetProperty="(Left)">

                    <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

                    <EasingDoubleKeyFrame KeyTime="00:00:00.50" Value="100"/>

                    <EasingDoubleKeyFrame KeyTime="00:00:00.80" Value="225"/>

                    <EasingDoubleKeyFrame KeyTime="00:00:01.00" Value="350"/>

             </DoubleAnimationUsingKeyFrames>

                    ...

                    ...

 

       </Storyboard>

</UserControl.Resources>

 

 

Live demo

 

Download the sample code

AnimatedSilverHtmlHost.zip (83.45 kb)