Sample: WPF User Control in a Dialog System Application

The Customer + .NET WPF GridView User Control sample CustGridWPF.sln shows a WPF user control used by a Dialog System application. The sample shows how a WPF user control can be hosted, how it can have methods invoked and how it can fire events, which can be consumed in the Dialog System application, as illustrated in the following diagram:

There are two key points about WPF shown in this sample:

The sample is based on the original CustGrid sample from Dialog System. It has two projects: one is the original CustGrid sample and the second contains the code for a simple WPF DataGrid control written in COBOL. The DataGrid control is similar to the control used in the Customer samples (Customer +.NET User Control and Customer + .NET WinForm). Here, in this sample, the DataGrid is a WPF user control.

Note:

In this sample, the control is not a complete implementation of a grid control and the application is not a complete implementation. The sample is designed to illustrate the principles only.

WPFCustGrid Project

The WPFCustGrid project contains the original CustGrid project from Dialog System.

CustGrid.gs is the screenset, which now contains the DataGrid control instead of the original ActiveX grid control. In the same way as before, Dialog System communicates with the native code gridctrl.cbl, using callouts and callbacks.

GridCtrl.cbl handles the events generated by the DataGrid control. It invokes the COM code in the same way as it invoked the ActiveX code before. For example, the Delete-A-Row Section handles the delete-row event and invokes the COM code, as follows:

  INVOKE RowSet "GetSelect" USING theRow RETURNING Numeric-Value
  INVOKE RowSet "Remove" USING Numeric-Value

Where RowSet is defined in the COM interface for the WPF user control in ControlInterface.cbl, which is described later.

WPFGridViewUserControl Project

The WPFGridViewUserControl project contains two user controls: one WPF and one Windows Forms. Both controls are needed because the WPF user control is hosted as a Windows Forms user control (which is then wrapped as an ActiveX control). The content is actually a WPF control, but it is handled as a Windows Forms user control, just as in the sample Customer + .NET User Control.

To see the two controls, double-click:

  • WPFUserControl.xaml - The properties show that the control is a WPF control, of type DataGrid. The XAML shows the definition of the DataGrid. The Document Outline shows the control as a DataGrid. Document Outline is available from View > Other Windows.
  • GridViewControl.cbl [Design] - The properties show that the control is of type Windows.Forms.ElementHost. The Document Outline shows the control as GridHost of type ElementHost. The Toolbox lists this type of control under WPF Interoperability, as ElementHost.

The project contains the following to complete the implementation:

  • CustomerOrder class, which maps to a CustomerOrder group item in the data block, in CustomerOrder.cbl
  • Class interface, MicroFocus.VisualCOBOL.IWPFSampleGridView in ControlInterface.cbl
  • Class WPFSampleGridView in GridViewControl.cbl
  • Events interface, IWPFGridViewEvents in GridViewEvents.cbl
  • RowSet class, which contains the methods to handle a row of the grid, in RowSet.cbl.

The project is registered for COM interop. In addition the COMRegister() method puts the appropriate entries into the registry so that ActiveX containers see this as a control. This method is covered in more detail later.

ControlInterface.cbl

ControlInterface.cbl defines the interface that exposes the methods and properties of the control to COM:

  interface-id  MicroFocus.VisualCOBOL.IWPFSampleGridView
      attribute  Guid("77BECD4D-5504-442D-9DDA-A78C30ADF6A9")
      attribute  InterfaceType(type  ComInterfaceType::InterfaceIsDual)
      attribute  ComVisible(true).
  method-id  get  property  RowSet.
  ...

GridViewControl.cbl [code]

GridViewControl.cbl contains the class that defines the behavior of the DataGrid control. Its main task is to replicate the behavior of the old ActiveX control in the new DataGrid control.

GridViewControl.cbl includes the following code of interest:

WPFSampleGridView

Defines the WPFSampleGridView class, which inherits from the Windows Forms UserControl class and implements the IWPFSampleGridView interface declared in ControlInterface.cbl. It also implements the ISerializable interface, which enables the class to control its serialization behavior.

  Class-id WPFSampleGridView
      inherits type System.Windows.Forms.UserControl
      implements  
        type System.Runtime.Serialization.ISerializable
        type MicroFocus.VisualCOBOL.IWPFSampleGridView
attribute Serializable()

Enable the class to be serialized. This attribute is required even if though the class also implements the ISerializable interface to control the serialization process.

  attribute Serializable()
attribute ComVisible(true)

Make the managed class visible to COM. Exposes the class to COM:

  attribute ComVisible(true)
attribute ProgId

Specify the ProgID for the COM object. This is a human-readable version of the class identifier (CLSID) used to identify COM/ActiveX objects:

  attribute ProgId("WPFSampleGridView")
attribute ClassInterface

Generate a class interface that supports early and late binding:

  attribute ClassInterface(type ClassInterfaceType::None)
attribute ComDefaultInterface

Specify the interface ISampleGridView as the default interface to expose to COM:

  attribute ComDefaultInterface
  (type of MicroFocus.VisualCOBOL.IWPFSampleGridView)
attribute ComSourceInterfaces

Identify the IGridViewEvents as the interface to be exposed as COM event sources for the library WindowsFormsControlLibrary1.dll (which is built by the GridViewUserControl project):

  attribute ComSourceInterfaces (type of  
        MicroFocus.VisualCOBOL.IWPFGridViewEvents)
attribute Guid

Specify an explicit GUID. This is useful during the debugging phase, as it avoids a new GUID being generated each time you build and register:

  attribute Guid("E0A41600-4531-4E55-9B24-6D2F1CF8B106")
ObservableCollection of CustomerOrder Objects

Create an observable collection of CustomerOrder objects, which enables the backing data to be updated when the user interface changes (such as when the user edits the data):

 *> Create an observable collection of CustomerOrder
 *> objects so that the data grid receives notifications
 *> such as add, delete in order to refresh the list
  01 _customerOrders 
      type ObservableCollection[type CustomerOrder]
      value new ObservableCollection[type CustomerOrder]
      public
      property as "CustomerOrders".
SampleGridView class

The SampleGridView class provides the following methods and delegate definitions:

COMRegister()

This method adds an entry to the Registry for the specified object. It sets the appropriate entries to register the object as an ActiveX, so that ActiveX containers see this as a control. This enables Dialog System to list the ActiveX as one of the controls available for import.

The project property Register for COM Interop registers the object as COM, but does not register it as ActiveX.

COMUnregister()

The reverse of the COMRegister() method.

New()

These methods create an instance of the WPFSampleGridView. The default New() method initializes the instance and adds the specified data by invoking the appropriate methods: OnSelectionChanged(), OnCellEditEnding() and OnSourceUpdated().

OnSourceUpdated()

Handles the OnSourceUpdated event, determines the row updated and invokes the FireOnChanged() method.

OnCellEditEnding()

Handles the OnCellEditEnding event and invokes the FireOnChanged() method.

OnSelectionChanged()

Handles the OnSelectionChanged event, determines the row and invokes the FireRowSelected() method.

GetObjectData()

This is used as part of serialization of the control. However, this sample does not have anything to persist so this method is unused.

Event Handling methods
  • FireOnChanged() - handles the OnChanged event
  • FireRowSelected() - handles the OnRowSelected event
  • FireRowDeleted() - handles the OnRowDeleted event
  • OnRowEnter() - invokes the FireRowSelected() method
Get property RowSet

Implements the interface IWPFSampleGridView. Creates an instance of the row.

Get property OrderGrid

This enables the Windows Forms user control to access the WPF DataGrid user control.

  method-id. get property OrderGrid.
  procedure division returning thegrid as 
        type System.Windows.Controls.DataGrid.
    set thegrid to self::wpfUserControl1::OrdersGrid
WPFSampleGridView_Load()

Handles the cell editing event.

  method-id OrdersGridView_CellEndEdit final private.
  procedure division using by value 
      sender as object 
      e as type System.EventArgs.
Delegate definitions

Defines the delegates for our events:

  01 OnRowSelected type RowSelectedEventHandler event public.
  01 OnRowDeleted type RowDeletedEventHandler event public.
  01 OnChanged type CellEditEndingHandler event public.
	 ...
  delegate-id RowSelectedEventHandler.
  delegate-id RowDeletedEventHandler.
  delegate-id CellEditEndingHandler.

GridViewEvents.cbl

IWPFGridViewEvents.cbl defines an interface for the events. The interface has the ComInterfaceType.InterfaceIsIDispatch attribute, which restricts callers to late binding.

  interface-id MicroFocus.VisualCOBOL.IWPFGridViewEvents
      attribute Guid("3DC45DA1-A580-4280-A2DB-7B6A266032AE")
      attribute InterfaceType
         (type ComInterfaceType::InterfaceIsIDispatch)
      attribute ComVisible(true).

The interface defines a method for each event, and assigns each event a COM dispatch identifier (DispId), as follows:

  method-id OnRowSelected attribute DispId(29).
  ...
  method-id OnRowDeleted attribute DispId(18).
  ...
  method-id OnChanged attribute DispId(6).

The events correspond to the delegates defined in the WPFSampleGridView class in GridViewControl.cbl.

Note, this interface is not implemented in our solution.

CustomerOrder.cbl

CustomerOrder.cbl defines the CustomerOrder class, which contains methods to create and initialize an instance of a customer order, and to return an array of customer orders.

The CustomerOrder class demonstrates a key concept in WPF, data binding. It demonstrates how the data can be associated with the user interface.

The CustomerOrder class maps to a Customer Order group item in the data block, so part of the sample gets data from the data block and creates a list of Customer Orders. This object is then associated with the WPF DataGrid, which renders the control to match the data in the list. The object is defined in GridViewControl.cbl, as the type ObservableCollection.