Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

CurionLib Dynamic Data Entry Forms

5.00/5 (20 votes)
11 Aug 2013CPOL16 min read 56.5K   5.6K  
Dynamic data forms.

Introduction

One feature of CurionLib creates a simple data entry form fairly quickly.  On the simple side, its data meets control, out pops a ui.  It supports editing a single object as well as collections of objects.  It's fairly extensible and was designed to allow a fair amount of design-time and run-time customization.

This article and two others are included in the documentation directory of the library.

There are four sample applications.  Demo.plain and Demo.mui use Demo.common and have the same content. Demo.plain has very little styling.  Demo.mui uses Modern.UI for styling.  Demo.designer shows a several settings that can be used to customize a form.  Demo.tiny is a small app that is one step beyond the sample app below.  It shows using a collection, navigating, adding, deleting, and custom commands. Image 1
Image 2 Image 3

Getting Started

The proof, as they say, is in the pudding.  So what does it actually take to get a form off the ground?

  1. Add a reference to CurionLib.dll.
  2. Add a ItemForm control to a Window or User Control:
    • If you will be adding many of them, you can add the controls to the tool palette by right clicking, selecting Choose Items and picking the CurionLib.dll.  Once in the toolbox, you can simply drag onto a form
    • Add a namespace to the definition:
      XML
      xmlns:curion="http://curion.net/Forms"

      Add the form:

      XML
      <curion:ItemForm x:Name="ctlForm" />
  3. Create a class.
  4. Set ctlForm.DataContext to an instance of the class.

Here's the completed Window:

XML
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:curion="http://curion.net/Forms"
x:Class="MainWindow"
Title="MainWindow" Height="140" Width="320">
<Grid>
<curion:ItemForm x:Name="ctlForm" />
</Grid>
</Window>

Here's a quick sample class:

VB.NET
Class Person
Public Property Name As String
Public Property Age As Integer
Public Sub Hello()
MsgBox(String.Format("Hello {0}.  I see you're {1} years old.", Name, Age))
End Sub
End Class

And the code on your form:

VB.NET
Class MainWindow 
Sub New()
InitializeComponent()
ctlForm.DataContext = New Person
End Sub
End Class

If all went well, you should see something like this when you run the app:

Image 4

Enter your name and age and click the Hello button and you should see a message box confirming your name and age.

Continuing a bit further, Demo.tiny extends the example by using a collection of people, adding navigation, add, delete, and display current record contents.  It looks like this:
Image 5

I know you're probably thinking, these weren't very exciting.  The good news is there's a bit more to the control, hang on a bit.

Organization

The code is organized in a few namespaces. 

  • Curion.WPF.Controls - Most of the controls. 
    • ActionStack - A custom control class that creates a list of buttons from anonymous methods.
    • CommandStack - A custom control class that creates a list of buttons for each method in the CommandSource with no parameters.
    • ContextDefaults - A class that localizes changes to Defaults.  Designed to be used in a Using block to temporarily change defaults for example when creating a form.
    • DynamicCommand - Creates a command for each no parameter method in CommandSource that can be executed by name.
    • Filter - A custom control class that is not intended to be used directly.  It is an ICollectionView filter control that consists of a Textbox (the filter), a TextBlock (the count of items), and a CommandStack (optional buttons that do stuff).
    • FilterList - A custom control class that filters items in a ListBox based on the text entered.
    • MappedContentButton - A factory class that creates a button and can optionally use mapped values or functions to create content for the buttons.
    • MemoryDefaults - A class that implements IDefaults.  A memory based implementation of Defaults which is used to supply initial values for various classes.
    • mSupport - A few shared support routines.
    • MyCornerRadiusConverter - Convert corner radius to and from 1, 2, or 4 values.
    • MyGridLengthConverter - Convert grid length to and from string. Supports A=Auto, #*=Star lengths, #=Pixels.
    • MyThicknessConverter - Converts thicknesses to and from 1, 2, or 4 values.
    • NamedBrushConverter - Converts solid brushes using hex, color names, and windows system color names to and from strings
    • NamedColorConverter - Converts colors using hex, color names, and windows system color names to and from strings
    • SimpleButton - A factory class that creates a basic button.
    • StringTitleConverter - Converts csv separated alignment and text to and from a FrameworkElement. ex: Center,My Title or Right,My Title
  • Curion.WPF.Controls.Form - Classes used by the form controls. 
    • BooleanCheckBoxColumnFactory - A factory class that creates a checkbox control.
    • BooleanToggleColumnFactory - A factory class that creates a toggle button control.
    • BorderColumnFactory - A factory class that creates a border control.
    • ButtonColumnFactory - A factory class that creates a button control.
    • ColorColumnFactory - A factory class that creates a color textbox control.
    • ComboBoxColumnFactory - A factory class that creates a combo box control.
    • CornerRadiusColumnFactory - A factory class that creates a corner radius text box control.
    • DateColumnFactory - A factory class that creates a date picker control.
    • DecimalColumnFactory - A factory class that creates a decimal text box control.
    • DoubleColumnFactory - A factory class that creates a double text box control.
    • ExpanderColumnFactory - A factory class that creates an expander control.
    • GridLengthColumnFactory - A factory class that creates a gridlength text box control.
    • GroupBoxColumnFactory - A factory class that creates a group box control.
    • HeadingColumnFactory - A factory class that creates a heading control.
    • HorizontalAlignmentColumnFactory - A factory class that creates a horizontal alignment combo box control.
    • ImageColumnFactory - A factory class that creates an image control.
    • IntegerColumnFactory - A factory class that creates an integer text box control.
    • IntegerSliderColumnFactory - A factory class that creates an integer slider control.
    • ItemForm - A custom control class for a single item form.
    • ItemsForm - A custom control class for a form with a collection of items.
    • ListBoxColumnFactory - A factory class that creates a list box control.
    • LongColumnFactory - A factory class that creates a long control.
    • MakeCsvColumns - A factory class that creates a list of control factories for each read/write property matching a csv list using mappers.
    • MakeCsvForm - A factory class that lays out controls for a form using a special line by line csv layout.  csv layouts allow placing multiple controls on a single line.  May contain a title and places; headings (#), horizontal rules (-), blank lines (_), buttons ({[-|+|*][header=]methodName}) and columns (columnName[=width]) using the csv layout.
    • MakeSimpleColumns - A factory class that creates a list of control factories for each read/write property using mappers.
    • MakeSimpleForm - A factory class that lays out controls for a form from top to bottom.  May contain a Title if specified and will layout buttons on the form (on top by default).  Use in conjunction with MakeCsvColumns to limit the columns displayed or change the prompts.
    • MakeSimpleHeader - A factory class that makes a label for use as a header on a form .
    • MakeXmlForm - A factory class that lays out controls using a simple xml layout.  xml layouts allow creating boxed regions.  The regions (group) can be oriented vertically or horizontally (columns) and place controls within the region by using a csv layout (csv).  Regions may be GroupBox, Expander, Border, or Clear (no decoration) regions.  The csv layout is from MakeCsvForm.
    • ManageArrayItems - Manages adding, deleting, and retrieving a view for an Array collection.
    • ManageArrayListItems - Manages adding, deleting, and retrieving a view for an ArrayList collection.
    • ManageClassColumns - Manages retrieving a list of columns, getting column type, getting and setting column values for an object.
    • ManageCollectionItems - Manages adding, deleting, and retrieving a view for a Collection collection.
    • ManageCollectionView - Manages adding, deleting, and retrieving a view for a CollectionView collection.
    • ManageDataListItems - Manages adding, deleting, and retrieving a view for a Curion DataList collection.
    • ManageDataListRowColumns - Manages retrieving a list of columns, getting column type, getting and setting column values for a curion data list row.
    • ManageDataRowColumns - Manages retrieving a list of columns, getting column type, getting and setting column values for a data row.
    • ManageDataTableItems - Manages adding, deleting, and retrieving a view for a DataTable collection.
    • ManageDataViewItems - Manages adding, deleting, and retrieving a view for a DataView collection.
    • ManageDictionaryColumns - Manages retrieving a list of columns, getting column type, getting and setting column values for a dictionary of values.
    • ManageDynamicObjectColumns - Manages retrieving a list of columns, getting column type, getting and setting column values for a dynamic object.
    • ManageExpandoObjectColumns - Manages retrieving a list of columns, getting column type, getting and setting column values for an expando object.
    • ManageGenericItems - Manages adding, deleting, and retrieving a view for a generic collection aka List(Of Type), ObservableCollection(Of Type), etc.
    • MarginConverter - Convert margin to and from 1, 2, or 4 string values.
    • MemoColumnFactory - A factory class that creates a memo field control.
    • SelectIManageColumns - Select column manager.
    • SelectIManageItems - Select collection / items manager.
    • SingleColumnFactory - A factory class that creates a single text box control.
    • SolidBrushColumnFactory - A factory class that creates a solid brush text box control.
    • StringTitleColumnFactory - A factory class the creates a title textblock control from text and alignment
    • SuspendFormGeneration - Class that suspends form generation, designed to be used in a using block while initially configuring a form.
    • TextBoxColumnFactory - A factory class that creates a text box.
    • ThicknessColumnFactory - Convert thickness to and from 1, 2, or 4 string values.
    • DateRules, DecimalRules, DoubleRules, IntegerRules, LongRules, SingleRules, TextRules - Classes that implement basic validation rules for data entry controls
  • Here's a quick reference of the form classes: Image 6
  • Curion.Data - Some miscellaneous data classes
    • DataFilter - A data class that provides column names, values, and parameter names.  Used by SQLTable to filter data.
    • Parameter - A data class that provides column name and value.  Used to pass parameters to SQL Server.
  • Curion.Data.Sql - Some SQL Server support classes
    • SqlTable - A class that provides a simplified method of creating a DataList for a SQL Server table and saving changes.
    • SqlSupport - A module with some SQL Server support routines.
  • Curion.Data.List - Classes that support DataList
    • DataList - A dynamic object class similar in usage to a DataTable.
    • Column - A class that represents a column in a DataList.
    • Row - A class that represents a row in a DataList.
    • ColumnCollection - A class containing a collection of Column for a DataList.
    • RowCollection - A class containing a collection of Row for a DataList.
    • CompareRow - A class that compares two rows of a DataList using a list of OrderItem.  Used when sorting a DataList.
    • OrderItem - A class that tracks a column name and sort direction.  Used when sorting a DataList.

In addition to the classes, there are a number of interfaces used:

  • IMakeButton - An interface for making a button.  Used by button creating classes.  MappedContentButton and SimpleButton implement this.
  • IMakeControl - An interface for making a control.  Used by the IMakeColumns interface.
  • IFilterView - An interface for filtering a view.  FilterText and View will be set and then the Filter predicate will be retrieved and used to filter the view.  Used by ItemFormFilterViewFields implements this.
  • IDefaults - An interface defines how Defaults are accessed.  ContextDefaults and MemoryDefaults implement this.
  • IMakeColumns - An interface for creating column factories.  MakeSimpleColumns and MakeCsvColumns are implementations of this.
  • IMakeForm - An interface for creating form factories.  MakeSimpleForm and MakeCsvForm as implementations of this.
  • IMakeHeader - An interface for creating header factories.  MakeSimpleHeader is an implementation of this.
  • IGetData - An interface for retrieving data.  SqlTable is an implementation of this.
  • IHandleChanges - An interface for handling changed data.  SqlTable is an implementation of this.

Terms

Here's what I mean by a few of the terms I've used:

  • Defaults - Defaults are a collection of values used to drive the initial properties of new classes.  Defaults can be changed globally or in a limited scope.
  • Factory - Factories are classes or methods that create classes, methods, or values.
  • Mapper - Mappers select items to be used in a particular context.  For example in the MakeSimpleColumns class, the ColumnMapper maps data types to column factories and the ColumnMapperException maps specific column names to column factories.  In this application, most mappers map a key, such as a datatype or column name, to a function.  Using anonymous methods, this allows a high degree of customization of the factory instantiation.
  • Header - The prompt that is normally displayed before a field.
  • Column - A factory that creates a .NET control.

What Does It Work With

ItemForm was designed to work with a single object.  To use a single object, set the DataContext property.  It has been designed to work with a POCO (plain old CLR object, i.e. your class), ExpandoObject, DataRow, DataList Row, DynamicObject, or Dictionary.

ItemsForm was designed to work with collections of objects supported by ItemForm.  To use a collection, set the ItemsSource property.  The View property will be available that will return the current ICollectionView in use.

For POCO collections, each item can be of a different type and a new UI will be created for each.  While this can be pretty cool in certain circumstances, one of the downsides is that RepeatButtons don't really work well, since they're re-generated for each new class / form.

Using the Control

There are two demo applications that show the controls in use.  DemoApp is a generally unstyled WPF application to give you a feel for what it looks like out of the box.  Wherever possible, I've used control wrapping to extend controls as opposed to inheriting controls.  The result of this is that it should be fairly easy to use your Theme with the control.  DemoAppMUI is an example of using a theme, MUI or Modern.UI in this case.

Both applications use Customer.xml in a few places.  It's a small XML file with 1,000 randomly generated customers and a few data types.

DemoApp

Shows using single objects, lists of objects with a custom pager, and lists of objects using a ListBox pager.  Madness demonstrates using a list containing multiple types with one or more methods.  And finally Separate command object demonstrates some basic overall form customization and using mapped content buttons.

All of the forms except Madness use the customer.xml records and can be displayed as a simple form, csv layout, or xml layout form by selecting the corresponding option button in the upper left corner of the app.

DemoAppMUI

Demonstrates the use of single and multi-item lists with theming which is provided by mui.  The pages that are displayed are in the Pages folder and organized roughly the same as the menu.  Of particular note:

  • Custom/CSV form which shows many of the features including headers, listboxes, options, and mapping to enums.
  • Custom/List Nav and List Nav2 which show customizing separators.
  • Custom/MiscControls which shows a filtered list box.
  • And utility which is another example similar to the first one that generates basic dependency properties.  It also demonstrates creating a default button.

MakeCsvForm

One of the controls with a good deal of customization is the MakeCsvForm control's layout.  Here's a quick overview of the meaning of the lines.  Note that text in [] are optional except as noted for the CommandButton.

    #HeadingDisplays a header.  Must be the first entry on a line and is considered to be the only entry on the line.
    - (dash)Draws a horizontal rule.  Must be the first entry on a line and is considered to be the only entry on the line.
    _ (underline)Puts in a blank line.  Must be the first entry on a line and is considered to be the only entry on the line.
    {button1[,button2...]}Places one or more CommandButtons (buttons for methods with no parameters) on a single line.  In the current implementation, buttons will grow up to 640 pixels wide and then wrap.  A button can have the following prefixes:
    • * - Create a repeat button.  Cannot be used with + or - prefixes.
    • + - Create a default button.
    • - - Create a cancel button.

    In addition, buttons take the form of either header=columnName or columnName.  If they take the second form, a simple conversion from Camel casing is done.  If a button name is omitted, spacing is inserted (i.e., First,,Last).  Multiple commas can be used to create increased spacing.  Must be the first entry on a line and is considered to be the only entry on the line.

    column1[=width][,column2...]Places one or more columns on a line of the form. column1 is assumed to be the name of a column in the current object.  width can take the following forms:
    • # - A fixed width in pixels, i.e., zipcode=80
    • #* - A proportional width in pixels.  ex: address=1*
    • A - Auto-sized to contents.

    If omitted, a width of 1* is assumed.

DemoApp uses the following layout:

Name,Phone=100
-
Address
City,State=50,Zipcode=80
-
Balance=100,Added=100,IsActive=23
{First,*Prior,*Next,Last,,Show}

Update 1.0.0.1

Added IntegerSlider, BooleanToggle, BorderColumn, ExpanderColumn, GroupBoxColumn, MakeXmlForm.  Two breaking changes header character was changed from > to # for csv layouts, and button maker is now a factory function.  Other misc changes, see 0Changes.txt for more info.

MakeXmlForm Added MakeXmlForm

  • <cols [height="value"]> - Arrange child nodes as columns.
  • <box [width="value"] [title="value"] [type="expander|groupbox|border|clear"] [isopen="true|false"]> - Group child nodes in a box container specified by type.
  • <csv [width="width"]> - generate a csv layout.  InnerText is the csv layout.
Nodes not directly under a <columns> tag are assumed to be on subsequent rows.

Xml layout from sample applications

XML
<layout>
<cols>
<box title="Person" isopen="true" type="expander">
<csv>
Name,Phone=100
</csv>
</box>
<box title="Misc" width="214" isopen="true" type="expander">
<csv>
Balance=100
Added=100
IsActive=23
</csv>
</box>
</cols>
<box title="Address" type="expander">
<csv>
#Address
Address
City,State=50,Zipcode=80
</csv>
</box>
<csv>
_
{First,*Prior,*Next,Last,,Show}
</csv>
</layout>

Update 2.0.0.0

Changed version to 2.0 because of the number of breaking changes, title change, and splitting the form into two controls.  Here's some of the changes, see 0Changes.txt for the rest.

Breaking Changes

  • Renamed IMakeColumn to IMakeColumns to clarify intended use.
  • Moved ButtonMaker to ItemForm.
  • In MakeXmlForm, renamed linegroup to columns, container to box, and lines to csv.
  • Split and renamed DataForm. ItemForm has a DataContext takes a single item. ItemsForm has an ItemsSource with related routines. Should be more clear when using controls.
  • IMakeColumns.Create initial parameter changed from obj As Object to base As ItemForm. This was done to facilitate re-using ItemForms ability to return a list of column names and retrieve the values for those columns. This should ultimately also provide greater flexibility in writing column generators.
  • Updated the Create method of IMakeHeader interface to include an ItemForm and column name. Should allow for more customization of headers.
  • Changed command button delimeters from [] to {}.  Should make documentation easier to read...
Other changes of note:
  • Added ColumnNames to ItemForm, returns column names for current DataContext object.
  • Added default property Item(columnName As String) As Object to ItemForm. Can get/set property values of current DataContext.
  • Added IMakeControl and updated column generators (MakeCsvColumns, MakeSimpleColumns) to select IMakeControl interfaces instead of BaseColumnFactory.
  • Added navigation and Add, Delete to ItemsForm.
  • Added some converters and factories for new column types.
  • Added IManageItems and ISelectManageItems to handle collection navigation, adding, deleting, and retrieving a view.
  • Added IManageColumns and ISelectMangeColumns to handle retrieving column names, types, and getting, setting column values.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)