Thursday, September 16, 2004

BizTalk 2004 Contest Winners

Scott Woodgate has the list of the BizTalk 2004 developers content winners here:

http://blogs.msdn.com/scottwoo/archive/2004/09/14/229601.aspx


Tuesday, September 14, 2004







Control Events


Controls can have events that are associated with them. Suppose that we had a server control that contained a single button. The button click event could perform some processing directly, or the event could call a function in another client via an exposed public event. If we have a number of different pages that contain the server control on them, each page could hook a function to the event defined in the button. When the button click event is fired, the hook function on the page is called. The page can now determine what happens in response to a click event that occurred on the button within an embedded server control. It is the page's responsibility to wire the button click event to the hook function. Note that the server control (or the button within the control) has no knowledge of what client is consuming it. Each page within the application can determine the behavior of the control event via the exposed hook function on the page. Suppose we had the following in the ascx file for the control:

<table width="100%">
<TR>
<td align="right">
<asp:Button id="btnProceed" runat="server" Text="Proceed"></asp:Button>
</td>
<td align="left">
<asp:Button id="btnCancel" runat="server" Text="Cancel"></asp:Button>
</td>
</TR>
</table>

In the code behind for the control we would define two EventHandlers, one for each button:

public event EventHandler Proceed;
public event EventHandler Cancel;

The button events would then forward to the hook method associated with the Proceed, or Cancel event objects.

private void btnProceed_Click(object sender, System.EventArgs e)
{
Proceed(this, e);
}

private void btnCancel_Click(object sender, System.EventArgs e)
{
Cancel(this, e);
}

The page that contains the control, sets the OnProceed and OnCancel events on the control to the hook functions in the code-behind of the page:

<%@ Register TagPrefix="ctrl" TagName="Buttons" Src="Buttons.ascx" %>

...

<ctrl:buttons id="NavigateButtons" runat="server" OnProceed="Page_Proceed" OnCancel="Page_Cancel"></ctrl:buttons>

The code behind of the page contains the hook functions Page_Proceed and Page_Cancel. Note that the hook functions are protected. The generated class that derives from the code behind will need to access the two hook functions, so they are declared with protected visibility.

protected void Page_Proceed(object sender, System.EventArgs e)
{
// do something for proceed
}

protected void Page_Cancel(object sender, System.EventArgs e)
{
// do something for cancel
}

When the aspx page is parsed and the dynamic class is generated, the control properties OnProceed and OnCancel are translated into the following code (in the generated page class):

NavigateButtons.Proceed += new EventHandler(this.Page_Proceed);
NavigateButtons.Cancel += new EventHandler(this.Page_Cancel);

When the Button1_Click event in the control fires, the Page_Proceed method is called in the page. Each page can now define hook methods that should be called when the button click events on the contained control are fired. Each page defines the hook functions, and the control tag in the .aspx page is modified to associate the hook methods with events in the control:



  • Add the EventHandler declarations in the control

  • Add the hook functions in the parent page that should be called when the event in the control is triggered

  • Add the server control tag to the .aspx file associated with the page

  • Add the On... attributes to the server control tag and set the attributes to the appropriate hook function in the page. Make sure that the attribute is named the same as the declared event in the control, prefixed by On. So if the event field in the control is Proceed, the attribute on the control in the .aspx page should be in the format OnProceed="hook function name in page"
Now each page can define their own hook functions for the Proceed and Cancel events exposed by the control:

// page A
<ctrl:buttons id="NavigateButtons" runat="server" OnProceed="PageA_Proceed" OnCancel="PageA_Cancel"></ctrl:buttons>

// page B
<ctrl:buttons id="NavigateButtons" runat="server" OnProceed="PageB_Proceed" OnCancel="PageB_Cancel"></ctrl:buttons>

The functions within each page (PageA_Proceed, PageB_Proceed) do not have to be named uniquely. Both pages could contain a Page_Proceed and Page_Cancel hook functions, the only difference being that the Page_Proceed and Page_Cancel in each page would perform different operations.













Creating an XML driven web site


An XML driven website is a website that stores the content as XML, either in a file or in a database. When a user requests a page, the XML content is retrieved from the data store and rendered as HTML in the browser. The HTML is usually created by a transformation, such as XSLT, or the HTML can be created programmatically. In either case, the XML data is merged with the markup to create the returned HTML page.

  public void GetContent(string loc)
  {
   XmlTextReader reader = null;
   
   try
   {
    reader = new XmlTextReader(Server.MapPath(loc));
    
    if (reader != null)
     RenderContent(reader);
   }
   catch(Exception e)
   {
    string err = e.ToString();
   }
  }


The nodes in an XML stream can be accessed by an XMLDocument, an XMLTextReader/XMLTextWriter and an XPathNavigator. Each method has its advantages and drawbacks:



  • XmlDocument: Provides direct memory access to the entire xml document. Nodes can be selected from the XmlDocument by XPath expressions. Can use a large amount of memory resources.

  • XMLTextReader: Provide forward-only access to an xml stream. The entire xml dataset is not loaded into memory all at once, the XmlTextReader reads in nodes on an as-needed basis. The XMLTextWriter writes xml data to a file in a forward-only manner.

  • XPathNavigator: Provides bi-directional access to nodes in a XmlDocument or XPathDocument. The XPathDocument provides a fast read-only cache for an xml document. If you are using an XmlTextReader to access the xml, an XPathDocument can be created by passing in a XmlTextReader object into the XPathDocument constructor. This object is not created directly, it is returned from a call to the CreateNavigator() method on the XmlDocument/XPathDocument class.


When inserting text into an XML file, beware of the special characters <, >, ', ", &. They should be escaped by using the special notation.


When passing XML data in memory you can use the following object types:


  • XmlNode. Provides an interface to an in-memory tree representation of an XML document. Gives a R/W interface to the XML data structure in addition to XPath expression selections. The XmlDocument inherits the XmlNode abstract class.

  • XmlReader. Streaming forward only object. Provides an xml facade over an existing object graph/text/binary data object. Difficult to use.


  • XPathNavigator. Traverses xml in a bi-directional manner. Able to navigate an xml document via XPath expressions. This is the preferred way to pass xml around in the CLR.















Delegates


Delegates are data types in the .NET framework that reference a method with a specific signature. Delegates are used to execute a method on a class via the created type. The ThreadStart type is an example of a delegate, in this case the ThreadStart delegate references a method that has an empty parameter list. You can create a ThreadStart delegate as follows:

  ThreadStart ts = new ThreadStart(DelegateFunction);
  Thread t = new Thread(ts);
  t.Start(); //start the thread
  
  for (int i = 0; i < 1000; i++)
   t.Sleep(5);
   
  


The ThreadStart class takes a reference to a method that cannot have any input parameters. The DelegateFunction is defined as:

  public void DelegateFunction()
  {
   // Do something...
  }
  


The DelegateFunction is executed when the Thread.Start function is run. In this case, the ThreadStart delegate wraps the method call, so that the method can be called via a type instance. Delegates are used for callback functions, events and as an implicit interface contract. A delegate can reference a function that takes parameters:

 public delegate string FunctionPointer(int foo, string bar);
 
 public FunctionPointer fpi; 
 fpi = new FunctionPointer(Function);
 


The Function declaration loooks like this:

 string Function(int foo, string bar)
 {
  string temp = "";

  for (int i = 0; i < foo; i++)
   temp += bar;
 
  return temp;
 }
 
The same delegate can point to another function with the same signature

 string AnotherFunction(int foo, string bar)
 {
  string temp = "AnotherFunction";

  for (int i = 0; i < foo; i++)
   temp += bar;

  return temp;
 }
 


The following is a sample console application that demonstrates delegates. In Main(), the source class and each sink class is created. The source class is the class that will call the function in the sink class via the delegate reference. Each sink class contains a method called Callback that will be called by the source class. To hook up the Callback method in the sink class to the delegate in the source class, a reference to the delegate is created and the name of the callback function in the sink class is passed as a parameter to the delegate reference constructor:

 using System;
 
 namespace Delegate
 {
  class Class1
  {
   [STAThread]
   static void Main(string[] args)
   {
    SourceClass sc = new SourceClass();
    SinkAClass sac = new SinkAClass();
    SinkBClass sbc = new SinkBClass();
 
    // Hook up the function and execute
    sc.fp = new SourceClass.FunctionPointer(sac.Callback);
    sc.DoSomething();
 
    // Hook up a new sink
    sc.fp = new SourceClass.FunctionPointer(sbc.Callback);
    sc.DoSomething();
   }
  }
 
  public class SourceClass
  {
   public delegate void FunctionPointer(int arg1, string arg2);
   public FunctionPointer fp;
 
   public void DoSomething()
   {
    // call the function pointed to by fp;
    fp(3, this.ToString());
   }
  }
 
  public class SinkAClass
  {
   public void Callback(int arg1, string arg2)
   {
    Console.WriteLine("{0} Callback method was called with arguments: {1}, {2}", this.ToString(), arg1.ToString(), arg2);
   }
  }
 
  public class SinkBClass
  {
   public void Callback(int arg1, string arg2)
   {
    Console.WriteLine("{0} Callback method was called with arguments: {1}, {2}", this.ToString(), arg1.ToString(), arg2);
   }
  }
 }







This page is powered by Blogger. Isn't yours?