Quality Interior Design
 
EzTools Software Logo Quality Windows Programming Tools
Because Presentation is Everything
   Access any SQL database through
your webserver with SqlWeb.NET
for .NET, Silverlight and Windows Phone 7
 
Home  Downloads  Buy Support Services About Us

EasyScript.NET

 

  Database Products
SqlWeb.NET DLL  
SQLitePlus & SQLite++  
FileDb Simple .NET Database  
  HSP System
Overview  
HTML Scripting Pages  
EzWeb Compiler  
EzWeb Copy Protection  
  ActiveX & .NET Components
EditListView Enhanced ListView  
WOW WebBrowser  
BrowserList HTML Listbox  
HotButton ActiveX  
HotLink HTML HyperLink  
HotList HTML Listbox  
SuperCombo HTML Droplist  
TabStrip ActiveX  
  Other Products
ConfigDb  
RegDb COM DLL  
Zip COM DLL  
EzComm Messaging DLL  
EasyScript.NET  

 

100% .NET Scripting Toolkit for .NET applications     
Back to EasyScript overview page

Dynamic WPF User Interfaces

EasyScript is a .NET scripting tool which you can use in your .NET programs for any scripting need. But you can also use it much like T4, PHP or Classic ASP to generate portions of your WPF or Silverlight pages dynamically at runtime. Your page becomes a template in which XAML and script code are mixed and run by EasyScript to generate your final page.

The main use of this method is for when you have a situation where you need to generate an unknown number of controls, for example for a query result of some sort. We encounter this situation all the time with websites anytime a query returns multiple results (e.g. a Google or eBay search), and its no different with everyday business programming. You can pass in .NET objects as parameters to the scripting context and use them directly in the script template as needed to generate your page. For example, you can pass in a DataTable parameter to use to generate a number of Hyperlink controls on the page using a loop. You run the script when its time to show the Page or Window, assigning the output of the script to the Content property of the Window. The cool thing is, you still use the Visual Studio or Blend page editor to create and maintain your pages. The key is you embed the script using XML comments (<!-- -->) so it doesn't interfere with the the XAML page editor.

Of course, you can dynamically add controls to the XAML page in code, but this is messy.  Server-side webpage scripting tools solve this problem by mixing code with the text of the page.  Each page is essentially a standalone program which is meant to generate an HTML page.  This is the same concept we use with EasyScript to generate a XAML page.

If you have already read the Overview page, you know how EasyScript works.  We will now show how to use EasyScript to generate XAML pages dynamically.

An Example

Let's use the sample download project for our example.  In this scenario, we have a form where the user can enter the first characters of a company name to display all customers which match the criteria.  Each customer is represented by a checkbox for the user to tick.  When the Submit button is clicked the customers with ticked checkboxes are discovered for further processing.  Here is a screenshot showing all customers whose names begin with letter A.

If there are too many checkboxes to fit on in the space, a scrollbar will appear on the page, just like on a webpage. The checkboxes were generated dynamically with the script shown here, which we will explain below.

<ScrollViewer Margin="0,0" Name="scrollViewer1" VerticalScrollBarVisibility="Auto"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  <Grid>
    <StackPanel Name="stackPanel1" Margin="0,0">
    <!-- ds.MoveFirst();
         cols = ds.Columns;
         n = 0;

         while( !ds.eof )
         {
             n++;
             compName = cols.GetColumn_("CompanyName").Value;
             compName = compName.Replace( "&", "&amp;" );

             Host.Write( "<CheckBox Height='16' Name='cb" + n.ToString() + "' Width='auto'>" );
             Host.Write( compName );
             Host.WriteLn( "</CheckBox>" );
             ds.MoveNext();
         } -->

    </StackPanel>
    <Button Height="32" Name="BtnClose" Width="62" HorizontalAlignment="Right" Margin="0,0,6,6"  VerticalAlignment="Bottom">Close</Button>
    <Label Height="26" Margin="0,0" Name="label1" VerticalAlignment="Top">Enter search string (e.g. A)</Label>
    <TextBox Height="23" Margin="6,22,133,0" Name="TxtSearch" VerticalAlignment="Top" />
    <Button Height="32" HorizontalAlignment="Right" Margin="0,0,6,62" Name="BtnSubmit" VerticalAlignment="Bottom" Width="62">Submit</Button>
    <Button Height="23" HorizontalAlignment="Right" Margin="0,22,48,0" Name="BtnSearch" VerticalAlignment="Top" Width="75">Search</Button>
  </Grid>
</ScrollViewer>

OK, so how does it all work?  The first thing to understand is that we still use the Visual Studio XAML page editor to create and maintain your XAML pages.  The key is that you enclose your script code in XML comments (<!-- -->) as shown below.

So the first thing you do is create your XAML page.  Then in the place where you need to generate your UI elements you insert your script code in comments.  Having the code enclosed in comments is required so that you don't get XAML compile errors.  Let's examine the script code which creates the checkboxes.


    <!-- ds.MoveFirst();
         cols = ds.Columns;
         n = 0;

         while( !ds.eof )
         {
             n++;
             compName = cols.GetColumn_("CompanyName").Value;
             compName = compName.Replace( "&", "&amp;" );

             Host.Write( "<CheckBox Height='16' Name='cb" + n.ToString() + "' Width='auto'>" );
             Host.Write( compName );
             Host.WriteLn( "</CheckBox>" );
             ds.MoveNext();
         } -->

 

We have passed the variable ds into the the script engine context.  It is a SqlitePlus Dataset (using our SqlitePlus COM DLL) which contains all of the customers returned from the query.  The code loops through the dataset, writing a CheckBox declaration for each row in the Dataset.

OK, but how do we run the script?  Obviously WPF will just ignore the comments in the XAML.  So what we do is copy everything in the XAML code except the Window declaration.  We will store this code as a text file resource in the project to be loaded and run when its time to show the window, as shown here:

I named the script files as .xaml.script, but it doesn't matter what you name it.  You are going to read this file in your code and feed it to EasyScript.  Then we will set it into the Content property of the Window.  Here are the steps to do that:

  • Execute the query which we will use to populate the page.
  • Read the script text from the embedded resource.
  • Instantiate an EasyScript.Scripter object and execute the script to generate the XAML.
  • Use the XamlReader to load the XAML and assign it to the Content property.
  • Assign control variables and wire up any events.

Here's the code. 


// In Form1.xaml.cs

public void Load()
{
  SqlitePlus.SqliteDb db = null;

  try
  {
    string searchText = string.Empty;

    if( Content != null )
    {
      TxtSearch = (TextBox) LogicalTreeHelper.FindLogicalNode( (DependencyObject) Content, "TxtSearch" );
      // get the search text, if any
      if( TxtSearch != null )
        searchText = TxtSearch.Text.Trim();
    }
    // get the requested customers
    db = MainWin.GetOpenDb();
    string sql = string.Format( "SELECT * FROM Customers WHERE CompanyName LIKE '{0}%'", searchText );
    SqlitePlus.ErrorCodeEnum ec;
    string sErr;
    SqlitePlus.Dataset ds = db.Execute( sql, null, out ec, out sErr );

    // add the Dataset to the script context
    List<Parameter> parameters = new List<Parameter>();
    parameters.Add( new Parameter( "ds", ds ) );

    // Load the script from the resource
    StreamResourceInfo info = Application.GetResourceStream(
                                new Uri( "Scripts/Form1.xaml.script", UriKind.Relative ) );
    TextReader reader = new StreamReader( info.Stream );
    string script = reader.ReadToEnd();

    // get an EasyScript Scripter object
    Scripter scriptObj = new EasyScript.Scripter();
    // tell it to use start/end comment for its code delimiters
    scriptObj.StartScriptDelims = "<!--";
    scriptObj.EndScriptDelims = "-->";
    // Execute the script
    scriptObj.RunScript( script, parameters );

    // get the script output
    MemoryStream strm = new MemoryStream();
    StreamWriter writer = new StreamWriter( strm );
    writer.Write( scriptObj.Output );
    writer.Flush();
    strm.Seek( 0, SeekOrigin.Begin );

    // Load the XAML and assign to the Content
    DependencyObject rootElement = (DependencyObject) XamlReader.Load( strm );
    this.Content = rootElement;

    // re-assign the controls and wire up the event handlers
    TxtSearch = (TextBox) LogicalTreeHelper.FindLogicalNode( (DependencyObject) Content, "TxtSearch" );
    if( TxtSearch != null )
      TxtSearch.Text = searchText;

    BtnClose = (Button) LogicalTreeHelper.FindLogicalNode( rootElement, "BtnClose" );
    if( BtnClose != null )
      BtnClose.Click += BtnClose_Click;

    BtnSubmit = (Button) LogicalTreeHelper.FindLogicalNode( rootElement, "BtnSubmit" );
    if( BtnSubmit != null )
      BtnSubmit.Click += BtnSubmit_Click;

    BtnSearch = (Button) LogicalTreeHelper.FindLogicalNode( rootElement, "BtnSearch" );
    if( BtnSearch != null )
      BtnSearch.Click += BtnSearch_Click;
  }
  finally
  {
    if( db != null )
      db.Close();
  }
}
 

Notice we have put all of this code in the Load method.  We call this method each time the Search button is clicked.  Each time it is run, we are generating new content based on the search criteria and assigning it to be the Content of the Window.  Very simple.

Some Notes

In this system, we have the code in two places, the XAML window and the resource script file.  This is not ideal, but its not that bad either.  Of course the master code is the XAML file, which you would keep in your source code control system.  You only need to copy it to the script file when you make a change to the code.

Of course, you can also keep your scripts as separate (loose) files and use Scripter.RunScriptFile.  Or you could retrieve your scripts from a database, a WCF service or a webserver.

This example replaces the whole window's content, but you could also use the same technique with UserControls as well - a very powerful tool.  The sample project in the download demonstrates this technique as well.




 

Copyright © EzTools Software2001-2010 All rights reserved. Trademarks and Disclaimer:
All trademarks belong to their respective companies.