Friday, January 28, 2011

Post III – Access with Silverlight & Windows Workflow 4.0

First, I would like to personally thank everyone who has downloaded the CodePlex source code. These posts are forging new ground with Access and .NET. Your interest provides motivation to continue unlocking .NET features into Access.
This new post is focused on some code re-design, exploring Windows Workflow 4.0, showing basic HTML editing with embedded Silverlight and showing WPF in a Task Pane. I’ll find some time to provide more specific code details. For now, the source code contains:
·         How to use Windows Workflow 4.0 in Access. We have a simple example, but more complex WF4 workflows could be used within Access
·         Added EventConverterAttribute to convert ondataavailable HTML DOM event to more specific event such as XmlBridgeChanged event
·         Added XmlBridge() method that returns a schema derived from an ADO.NET DataSet. We considered using a Data Contract, but a DataSet XML Schema is easier to generate and is more in tune with Access. Future versions will use the DataSet schema to send/receive XML messages to/from Silverlight and Access.
·         Added a WPF Task Pane to show how to use WPF with Access
·         Modified Database Form Reflector to support more efficient Access object get and release. Note that the pattern used in Form.Reference.cs files always releases the Access process. See http://msdn.microsoft.com/en-us/library/aa902693(office.12).aspx that discusses this issue.
·         Added a simple SharePoint Client Object Model example.
·         Added a few HTML editor examples that use HTML contentEditable = true
·         Added Silverlight Treeview popup to Sample. Click popup link to show/hide Silverlight Treeview
·         Added more code documentation

Workflow

·         Added an example of how to use Windows Workflow 4.0 in Access. The example executes a custom CodeActivity that implements ITaskActivity. Each CodeActivity gets data from a different data source. Note that this is an example only. Not a very fancy sample, but shows the basic principles.


Access Addin

void WorkflowCommand_Click()
{
    
Cursor.Current = Cursors.WaitCursor;
      String fullFilePath = @"ExampleWorkflow.xaml";

      Activity wf = (Activity)ActivityXamlServices.Load(fullFilePath);

      try
      {

              IDictionary<String, Object> output = WorkflowInvoker.Invoke(
                     wf, new Dictionary<String, Object>
                    {
                        {"ArgTeamCode",  Convert.ToInt32(Tasks.ItemText.Value)},
                    });
       
Tasks.HtmlDocument.getElementById("popup").innerText = output["OriginatedCount"].ToString();
      }
      catch (InvalidOperationException exception)
      {
              MessageBox.Show(exception.Message.ToString());
      }
      finally
      {
              Cursor.Current = Cursors.Default;
      }
}

Example CodeActivity

public sealed class Activity1 : CodeActivity, ITaskActivity
    {
      public InArgument<Int32> TeamCode { get; set; }
      public OutArgument<Int32> CategoryCount { get; set; }
      public OutArgument<Int32> WaitingCount { get; set; }

      protected override void Execute(CodeActivityContext context)
      {
              NorthwindDataSetTableAdapters.CategoriesTableAdapter categories =
              new NorthwindDataSetTableAdapters.CategoriesTableAdapter();

CategoryCount.Set(context, Convert.ToInt32(categories.CategoryCount(TeamCode.Get(context)));

              WaitingCount.Set(context, 10);
      }
    }

interface ItaskActivity
{
       InArgument<Int32> TeamCode { get; set; }
       OutArgument<Int32> OriginatedCount{get; set;}
       OutArgument<Int32> WaitingCount{get; set;}

}

Silverlight

·         MyDocumentPanel changed to WebWidget
·         Added EventConverterAttribute to convert ondataavailable HTML DOM event to more specific event such as XmlBridgeChanged event.

Example:
[EventConverter(“XmlBridgeChanged”)]
Tasks.TreeviewPage.onXmlBridgeChanged += new EventHandler<AsyncEventArgs>(TreeviewPage_onXmlBridgeChanged);
·         Added XmlBridge() method that returns a schema derived from an ADO.NET DataSet. Considered using a Data Contract, but a DataSet XML Schema is easier to generate. Future versions will use the DataSet schema to send/receive XML messages to/from Silverlight and Access.

[ondataavailable, EventConverter("onXmlBridgeChanged")]
public void XmlBridge()
{
          HtmlElement xmlBridge = HtmlPage.Document.GetElementById("xmlBridge");
          if (xmlBridge != null)
          {
              xmlBridge.SetProperty("innerHtml", Resource.XmlSchema.ToString());            

htmlBridge.ondataavailable(new HtmlBridge.AsyncEventArgs("XmlBridge", "TreeviewPage", "XmlBridgeChanged"));
          }
}    

WPF Task Pane

·         Added a WPF Task Pane to show how to use WPF with Access

 Database Form Reflector

·         Modified to support more efficient Access object get and release. Note that the pattern used in Form.Reference always releases the Access process. See http://msdn.microsoft.com/en-us/library/aa902693(office.12).aspx that discusses this issue.

SharePoint

·         Added a simple SharePoint Client Object Model example.

Access Addin

void SharePointCommand_Click()
{

      Cursor.Current = Cursors.WaitCursor;

      ClientContext clientContext = new ClientContext("http://localhost");
      Web site = clientContext.Web;
      clientContext.Load(site);
      clientContext.ExecuteQuery();
      Cursor.Current = Cursors.Default;
      MessageBox.Show(site.Title);
}

Web Browser Control

·         Added a few HTML editor examples that use HTML contentEditable = true

Access Addin

void ItalicCommand_Click()
{
      Tasks.HtmlDocument.execCommand("Italic");
}

void BoldCommand_Click()
{
      Tasks.HtmlDocument.execCommand("Bold");
}

void Heading1Command_Click()
{
      Tasks.HtmlDocument.execCommand("Heading1");
}

Thursday, January 27, 2011

Post III source code is coming in a few days

The source code:
·         How to use Windows Workflow 4.0 in Access. We have a simple example, but more complex WF4 workflows could be used within Access
·         Added EventConverterAttribute to convert ondataavailable HTML DOM event to more specific event such as XmlBridgeChanged event
·         Added XmlBridge() method that returns a schema derived from an ADO.NET DataSet. We considered using a Data Contract, but a DataSet XML Schema is easier to generate and is more in tune with Access. Future versions will use the DataSet schema to send/receive XML messages to/from Silverlight and Access.
·         Added a WPF Task Pane to show how to use WPF with Access
·         Modified Database Form Reflector to support more efficient Access object get and release. Note that the pattern used in Form.Reference.cs files always releases the Access process. See http://msdn.microsoft.com/en-us/library/aa902693(office.12).aspx that discusses this issue.
·         Added a simple SharePoint Client Object Model example.
·         Added a few HTML editor examples that use HTML contentEditable = true
·         Added Silverlight Treeview popup to Sample. Click popup link to show/hide Silverlight Treeview
·         Added more code documentation

Monday, December 20, 2010

Using Silverlight with Access – Part III (What's Next)

The part II release needs some improvements. So, sometime next month, we will release part III with a focus on
1.       Improving the HtmlBridge Silverlight Reflector
a.       The current Silverlight reflection class does not support all primitive types. The next version will reflect on all primitive types.
b.      The MSHTML Table Object Model will be used to support Table data to/from Access/Silverlight using an IHTMLTable bridge. See example below.
c.       The current version only supports void methods. The next version will support all primitive return types as well as an IHTMLTable bridge return type.
2.       Adding more Treeview features. Not sure what we will add at this time, but we want to show more Silverlight control code.
3.       Improving source code documentation.
4.       Improving ActiveForm.Proxy as needed.

Add Access Recordset Value to TreeviewPage.Table Example
mshtml.IHTMLTable rootTable = 
(mshtml.IHTMLTable)MyDocumentTasks.TreeviewPage.Table(“RootItems”);
 mshtml.HTMLTableRow rootItem = rootTable.insertRow();
mshtml.HTMLTableCell cell = myNewRow.insertCell();
//Assign Recordset value to table cell value
cell.innerText = rst.Fields["ProductName"].Value;
//Refresh Treeview
MyDocumentTasks.TreeviewPage.Table(“RootItems”).Refresh();

Wednesday, December 15, 2010

Using Silverlight with Access – Part II

The first release was more of a concept. The focus of this release was to improve the Silverlight control and Database form coding experience to reduce the time required to create the Silverlight / Access integration. The release simplifies wiring together Silverlight and an Access Database form. We hope the changes result in less code to write. The next blog will show more Silverlight coding.

What we have been doing

The goal of this release was to enable the ability to auto-generate Silverlight Control and Database Form reference classes with minimal lines of code. To accomplish this goal, assembly and database form reflection concepts were incorporated to auto-generate common class patterns.
Release Highlights
·         Added WidgetRelector.Transform() that transforms Silverlight reflection XML into a client-side C# class.
·         Added ActiveForm.Proxy.Transform() that transforms a database form into a C# class. Useful for any Access addin project.
·         Added Generate Reference Files dialog form. The form uses Silverlight.Emit(), WidgetRelector.Transform() and ActiveForm.Proxy.Transform() to auto-generate C# reference proxy files.
·         Enhanced TreeviewPage.AddItem(header). The method now adds a child TreeViewItem to any selected node.
·         Added TreeviewPage.RemoveItem() that removes a TreeViewItem.
·         Added an abstract HtmlBridge class that defines recommended .NET EventArgs classes. HTML DOM events raised by Silverlight resemble standard .NET events.

Silverlight Control Changes

An IReflection interface was added that defines an Emit() method.

IRelection Interface

void Emit() Method
The Emit() method uses LINQ and Assembly reflection to emit XML describing ScriptableMembers and events available to HtmlBridge. The Emit() method is used to auto-generate a client-side Silverlight control proxy reference file.
How Silverlight reflection maps to Access / Silverlight Addin Reference class

XML
Access / Silverlight Addin Reference class
<class name="TreeviewPage">…
class TreeviewPage
<attribute name="HtmlBridgeEvent.onclick" />
public new event EventHandler<ElementEventArgs> onclick;
<class name="ElementEventArgs"> …
base.onclick += new EventHandler<ElementEventArgs>(TreeviewPage_onclick);
<method name="AddItem"> …
<parameter name="itemValue">
          <parameterType name="String" …
public void AddItem(String itemValue)
  base.InvokeScriptableMethod("AddItem('" + itemValue + "')");


Create a Silverlight Control proxy reference file
The follow example creates all client-side (Addin) Silverlight reference code.
WidgetAddinRelector r = new WidgetAddinRelector((mshtml.HTMLDocument)ActiveForm
.WebBrowserControls(ActiveForm.WebBrowser.Names[0].ToString()).Object.Document);

r.Emit();

Event Attributes
HtmlBridge event attribute classes were added to define class and member level event attributes. The attributes are used to emit which HTML DOM events are raised by a Silverlight control. For example, the onclickAttribute reveals to WidgetReflector that the Silverlight control raises an HTML DOM onclick event. WidgetReflector reflects on event attributes to auto-generate HtmlBridge event code.
How HtmlBridge attributes map to Access / Silverlight Addin Reference

HtmlBridge Event Attributes
Access / Silverlight Addin Reference
[ScriptableType, onclick, onmousedown]public partial class TreeviewPage : UserControl, IReflection
public new event EventHandler<ElementEventArgs> onclick;

public new event EventHandler<ElementEventArgs> onmousedown;

base.onclick += new EventHandler<ElementEventArgs>(TreeviewPage_onclick);

base.onmousedown += new EventHandler<ElementEventArgs>(TreeviewPage_ onmousedown);
[ScriptableMember, ondataavailable]
public void AddItem(string itemValue)
public new event EventHandler<AsyncEventArgs> ondataavailable;

base.onclick += new EventHandler<AsyncEventArgs>(TreeviewPage_ ondataavailable);


HtmlBridge Attribute

The HtmlBridgeAttribute defines EventArg classes exposed by a Silverlight control.
Example
[HtmlBridge]
public class ElementEventArgs : EventArgs
{
public string Qualifier { get; private set; }
                public string Name { get; private set; }
                 public string Text { get; private set; }
                 public string Tag { get; private set; }
                 public string Value { get; private set; }
Silverlight onclick Example
onclick(new HtmlBridge.ElementEventArgs(item.Header.ToString(), tag));
Access Addin
MyDocumentTasks.TreeviewPage.onclick += new EventHandler<ElementEventArgs>(TreeviewPage_onclick);
void TreeviewPage_onclick(object sender, ElementEventArgs e)

HtmlBridge Utility Class

An HtmlBridge utility class was added to simplify calling HtmlBridge scripting methods from a Silverlight control.

Silverlight Control
HtmlBridge
htmlBridge.onclick(new HtmlBridge.ElementEventArgs(item.Header.ToString(), tag));
[HtmlBridgeEvent]
public void onclick(ElementEventArgs eventArgs)
{
      FireEvent("onclick", eventArgs);
}

public void FireEvent(string eventName)
{         HtmlPage.Window.Eval(_elementName + ".fireEvent('" + eventName + "')");
}


Access Database Changes

WidgetReflector

A WidgetReflector class was added. The class is a primarily a LINQ class that transforms a Silverlight reflection Xml stream into a Silverlight control proxy reference class. The class auto-generates a Silverlight control proxy reference class hiding the Silverlight control plumbing details from the developer.
Usage
WidgetReflector.Transform(sourceXml, sourceCodePath)

ActiveForm

ActiveForm is a static class for an Access form object wrapper. The class is defined as static since Access creates object instances.

internal static class Proxy
Internal static class that transforms or auto-generates the ActiveForm into a .NET wrapper class. Note that the class has not been tested with all Access controls.

Usage
MyDataAddin.ActiveForm.Proxy.Transform();

The Transform() method auto-generates all database form proxy reference code. For example, calling Transform() on the MyDocumentTasks form auto-generates all code enabling simple implementation methods hiding the addin plumbing details from the developer.

Examples

Implementation Code
Auto-generated Database Form code
MyDocumentTasks.ItemText.Value
private static Microsoft.Office.Interop.Access.TextBox _itemText = null;
    public static Microsoft.Office.Interop.Access.TextBox ItemText
{
      get
      {
        return _itemText;
      }
}
MyDocumentTasks.TreeviewPage.AddItem(itemValue)
public static TreeviewPage TreeviewPage;
TreeviewPage = new TreeviewPage((mshtml.HTMLDocument)
_webBrowser.Object.Document);


HtmlBridge

Abstract class inherited by Silverlight control providing base HtmlBridge members.

MyDocumentTasks.Reference

Class is now auto-generated from ActiveForm.Proxy.Transform().

TreeviewPage.Reference

Class is auto-generated from WidgetReflector.Transform().

Configuration Form

Added Addin Widger Proxy Files ribbon button to Database Tools.
Addin Widget Proxy Files opens Generate Refenence Files dialog form. Create button creates Silverlight and Database form proxy reference file. The configuration form was added to Access as a Power Tool.



Running the Generate Reference Files tool hides the Silverlight / Access addin wiring. Thus, implementation code becomes less complicated.
After running the tool and pasting the code into a .NET class file, a developer can write code similar to the following examples.
Ribbon Button to Open Form
public void myDocumentTasksButton_Click(IRibbonControl control)
{
//Open Form with reference to Addin
MyDocumentTasks.Open(this);
             
       //AddItemCommand
MyDocumentTasks.AddItemCommand.Click += new access.DispCommandButtonEvents_ClickEventHandler(AddItemCommand_Click);
MyDocumentTasks.AddItemCommand.OnClick = bindEvent;
             
//RemoveItemCommand
MyDocumentTasks.RemoveItemCommand.Click += new access.DispCommandButtonEvents_ClickEventHandler(RemoveItemCommand_Click);
MyDocumentTasks.RemoveItemCommand.OnClick = bindEvent;

             
       //TreeviewPage.onclick
MyDocumentTasks.TreeviewPage.onclick += new EventHandler<ElementEventArgs>(TreeviewPage_onclick);
             
//TreeviewPage.ondataavailable
MyDocumentTasks.TreeviewPage.ondataavailable += new EventHandler<AsyncEventArgs>(TreeviewPage_ondataavailable);
}


Add Item Click Delegate
void AddItemCommand_Click()
{
MyDocumentTasks.TreeviewPage.AddItem(MyDocumentTasks.ItemText.Value);

}


Remove Item Click Delegate

void RemoveItemCommand_Click()
{
 MyDocumentTasks.TreeviewPage.RemoveItem();
}

Silverlight TreeviewPage onclick Delegate

void TreeviewPage_onclick(object sender, ElementEventArgs e)
{
MyDocumentTasks.TabControl.Value = e.Tag;
}

Enhanced Treeview Methods
We enhanced the AddItem() method. The method adds a child TreeViewItem to any selected item. We also added an AddRootItem() and RemoveItem method.