Dynamic Compilation, Reflection, & Customizable Apps
by David B. Scofield and Eric Bergman-Terrell

Example 1:

public class HelloToolbarButton : CustomToolBarButton
{
  public HelloToolbarButton()
  {
    Text        = "Hello World";
    ToolTipText = "Display Hello World Message";
  }
  public override Image GetImage()
  {
    return new Bitmap("World.bmp");
  }
  public override void DoAction(object sender, System.EventArgs e)
  {
    MessageBox.Show(iapi.TheMainForm, "World", "Hello");
  }
}


Listing One

// Compile the specified source code to an in-memory assembly.
public bool Compile(String sourceCode, out CompilerError compilerError)
{
  bool result = true;
  compilerError = null;
  // Only compile if source code has changed since previous compilation.
  if (sourceCode != compiledSourceCode)
  {
    CSharpCodeProvider cSharpProvider = new CSharpCodeProvider();
    ICodeCompiler codeCompiler = cSharpProvider.CreateCompiler();

    // Create a CompilerParameters object that specifies assemblies referenced
    //  by the source code and the compiler options chosen by the user.
    CompilerParameters compilerParameters =
      CreateCompilerParameters(
        SerializeConfiguration.Settings.assemblies);
    compiledSourceCode = null;
    try
    {
      // Compile the source code.
      compilerResults =
      codeCompiler.CompileAssemblyFromSource(compilerParameters, sourceCode);
      // Check for errors.
      if (compilerResults.Errors.Count > 0)
      {
        compilerError = compilerResults.Errors[0];
        result        = false;
      }
      else
      {
        // Keep track of the source code that was compiled.
        compiledSourceCode = sourceCode;
      }
    }
    catch (Exception)
    {
      MessageBox.Show("Cannot compile source code", Application.ProductName, 
                      MessageBoxButtons.OK, MessageBoxIcon.Error);
      result = false;
    }
  }
  return result;
}


Listing Two

public interface ICustomToolBarButton : IAPIUser
{
 ...
}
public class CustomToolBarButton : ICustomToolBarButton
{
  // Reference to the program's API.
  protected IAPI iapi;
  // The code that creates a CustomToolBarButton will call this method 
  // and pass in an object that implements the IAPI interface.
  public void SaveAPI(IAPI iapi)
  {
    this.iapi = iapi;
  }
  // Button's caption.
  private String text;
  public String Text
  {
    get
    {
      return text;
    }
    set
    {
      text = value;
    }
  }
  // Button's Tool-tip text
  private String toolTipText;
  public String ToolTipText
  {
    get
    {
      return toolTipText;
    }
    set
    {
      toolTipText = value;
    }
  }
  // Button's style
  public virtual ToolBarButtonStyle Style
  {
    get
    {
      return ToolBarButtonStyle.PushButton;
    }
  }
  // Button's graphic, no graphic by default.
  public virtual Image GetImage()
  {
    return null;
  }
  // Do the action to be performed when the button is pressed.
  public virtual void DoAction(object sender, System.EventArgs e)
  {
    MessageBox.Show(iapi.TheMainForm, text);
  }
}


Listing Three 

public interface ICustomMenuItem : IAPIUser
{
 ...
}
public class CustomMenuItem : ICustomMenuItem
{
  // Reference to the program's API.
  protected IAPI iapi;

  // The code that creates a CustomMenuItem will call this method and 
  // pass in an object that implements the IAPI interface.
  public void SaveAPI(IAPI iapi)
  {
    this.iapi = iapi;
  }
  // Menu's text
  private String text;
  public String Text
  {
    get
    {
      return text;
    }
    set
    {
      text = value;
    }
  }
  // Menu's shortcut, if any.
  private Shortcut menuShortcut = Shortcut.None;
  public Shortcut MenuShortcut
  {
    get
    {
      return menuShortcut;
    }
    set
    {
      menuShortcut = value;
    }
  }
  // Default click method
  public virtual void Click(object sender, System.EventArgs e)
  {
    MessageBox.Show(iapi.TheMainForm, text);
  }

  // Click event handler
  public virtual EventHandler ClickEventHandler
  {
    get
    {
      return new EventHandler(Click);
    }
  }
  // Default code to run when user highlights this custom menu item
  public virtual void Select(object sender, System.EventArgs e)
  {
    iapi.TheStatusBar.Text = text.Replace("&", "");
  }
  // Select event handler
  public virtual EventHandler SelectEventHandler
  {
    get
    {
      return new EventHandler(Select);
    }
  }
 ...
}


Listing Four

// Re-load toolbar with custom toolbar buttons
private void UpdateProgrammableToolbarButtons()
{
  // Remove custom toolbar buttons, they will be re-added below.
  customToolbar.Buttons.Clear();
  if (customToolbar.ImageList != null)
  {
    // Need to re-create the toolbar's image list 
    // or button graphics will be incorrect.
    customToolbar.ImageList.Dispose();
    customToolbar.ImageList = null;
  }
  CompilerError compilerError;
  if (!inMemoryCompiler.SyntaxErrorsExist(
         sourceCode.Code, out compilerError))
  {
    Type[] types = inMemoryCompiler.GetTypes(sourceCode.Code, true);
    foreach (Type type in types)
    {
      // Only look at classes that implement ICustomToolbarItem
      if (type.GetInterface("ICustomToolBarButton") != null)
      {
        ICustomToolBarButton item = 
          (ICustomToolBarButton) Activator.CreateInstance(type);
        item.SaveAPI(this);
        ICustomToolBarButton customToolbarButton = item;
        textBox.Text += "Adding custom toolbar button: " + 
                    item.GetType().FullName + Environment.NewLine;
        ToolBarButton button = new
                ToolBarButton(customToolbarButton.Text);
        button.Style = customToolbarButton.Style;
        Image image = customToolbarButton.GetImage();
        if (image != null)
        {
          if (customToolbar.ImageList == null)
          {
            customToolbar.ImageList = new ImageList();
            customToolbar.ImageList.ColorDepth       = 
                            ColorDepth.Depth4Bit;
            customToolbar.ImageList.TransparentColor = 
                            System.Drawing.Color.Green;
          }
          customToolbar.ImageList.Images.Add(image);
          button.ImageIndex = 
            customToolbar.ImageList.Images.Count - 1;
        }
        button.ToolTipText = customToolbarButton.ToolTipText;
        button.Tag         = customToolbarButton;
        customToolbar.Buttons.Add(button);
      }
    }
  }
}



Listing Five

// Return true if the specified class is nested inside of another class.
private bool TypeIsNested(Type type)
{
  return type.FullName.IndexOf('+') != -1;
}
// Clear and re-create custom menus.
private void UpdateProgrammableMenus()
{
  customMainMenu.MenuItems.Clear();
  CompilerError compilerError;
  if (!inMemoryCompiler.SyntaxErrorsExist(
         sourceCode.Code, out compilerError))
  {
    Type[] types = inMemoryCompiler.GetTypes(sourceCode.Code, true);
    ArrayList nonNestedMenuItems = new ArrayList();

    foreach (Type type in types)
    {
      if (type.GetInterface("ICustomMenuItem") != null &&
          !TypeIsNested(type))
      {
        nonNestedMenuItems.Add(type);
      }
    }
    GetChildMenus(customMainMenu, 
                 (Type[]) nonNestedMenuItems.ToArray(typeof(Type)));
  }
}
// Create a menu item and add subordinate menu items to it.
private void GetChildMenus(MenuItem ParentMenu, Type[] nestedTypes)
{
  foreach (Type type in nestedTypes)
  {
    // Only look at classes that implement ICustomMenuItem
    if (type.GetInterface("ICustomMenuItem") != null)
    {
      //Got one at the right level
      ICustomMenuItem item = 
        (ICustomMenuItem) Activator.CreateInstance(type);
      item.SaveAPI(this);
      CMI.ICustomMenuItem customMenuItem = item;
      //Get a new menu item and set the properties
      MenuItem newMenuItem = new MenuItem(customMenuItem.Text,
                          customMenuItem.ClickEventHandler);
      newMenuItem.Select += new
               EventHandler(customMenuItem.SelectEventHandler);
      newMenuItem.Shortcut = customMenuItem.MenuShortcut;
      newMenuItem.OwnerDraw = customMenuItem.IsOwnerDrawn;
      newMenuItem.MeasureItem  += new MeasureItemEventHandler(
                      customMenuItem.measureItemEventHandler);
      newMenuItem.DrawItem += new
        DrawItemEventHandler(customMenuItem.drawItemEventHandler);
      //Notification to the user
      textBox.Text += "Adding menu class: " + type.FullName + 
                Environment.NewLine;
      //Get the sub-menus for this item
      GetChildMenus(newMenuItem, 
                    type.GetNestedTypes(BindingFlags.Public));
      //Add this item to the parent
      ParentMenu.MenuItems.Add(newMenuItem);
    }
  }
}








6



