Click here to Skip to main content
Click here to Skip to main content

WinForms controls to develop with the Pfz.Databasing framework

, 7 Oct 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
Really easy to use framework capable of generating the right controls dynamically for each data type.

History

I recently presented the Pfz.Databasing framework. It was not very successful in its first presentation, but I think this new framework, based on it, will show how useful it is. This framework is able to work with serialized files, so making test projects is easier, and there are some improvements, but that's not the real thing to show.

Introduction

I already said that on the other article, but I must say it again: My idea is to create database interfaces and have everything created for me. If possible, the database, the forms, and even webforms must be done without coding. Well, the Pfz.Databasing does the part of creating the database and making searches and updates very easy. Pfz.Databasing.Controls tries to make creating the Windows Forms very easy.

How?

Well, the principle is simple, to create the database and to create the databound controls. Properties are discovered. The right DataTypeConverter is found to convert values to and from the database and the right Editor is found to display and edit the values.

But what happens with custom created types? Well, both the DataTypeConverters and the Editors are registered. So, if you create a new custom data type, you can create the new custom data type converter to read and write it to the database, and the right editor to show and edit the values. At first, this could look like, "Wow! That's too much work for so little", but after some time, the idea will sound wonderful.

Let's compare. One of the great advantages of Delphi is that you can create a basic database form very easily. You drag and drop the fields, and a basic form is created for you.

Actually, the framework does not have "drag and drop" possibility for all fields, but you can drop a control that represents a full record on the form, set the record type, and all the properties will be shown. Or, you can drag and drop a "PropertyBoundControl", set the record type and property, and the right thing will be shown.

So, what is really new about thi? It is dynamic. Instead of putting a TextBox on the form and having it as a TextBox forever, you put a control that is bound to MyRecord.MyProperty, which is an int. If I create a new editor for int, and don't need to reedit the form to put the new editor, the right one will be used. See how useful this can be in a giant system? Also, if you change the data type of the field to something more specific, but which is still compatible with the database, the editor for such a type will be shown, without the need to find where such properties are referenced. Now it looks better, doesn't it? And, to finish, if you create some type of "skin", create more than a control to show data, and allow the user to select which one to use, you only need to register the right control, and all the system will work with the new control for the user.

The controls

  • PropertyBoundControl - The only "database control" you need. Shows exactly one property (or database field, if you prefer). They also know how to show their own DisplayNames, if allowed.
  • RecordBoundControl - The control that shows an entire record, using many PropertyBoundControls internally. It also allows you to set what "columns" you would like to show and their minimum widths, allowing many of them to show in a single line. Makes creating simple layouts really easy.
  • RecordBoundGrid - The control capable of showing and editing many records at once. It creates many lines of PropertyBoundControls and also works in a CachedUpdates manner.

Also, to help create forms really fast, there is the FormEditRecord, with edits one record at a time, and FormEditRecords, which already has a grid and the buttons to add/delete records and apply/revert changes. You only need to set the record or records, and it is all working.

That's the easy part. The framework already includes controls for editing string, numbers, enums (showed as combobox), date/time with calendars, and boolean with checkboxes (of three states, if it is nullable), so creating basic forms is only a question of using these types. But, for the special types, there is a more complex part.

IPropertyBoundControlGenerator, PropertyBoundControlGenerator, and IPropertyBoundControl

To have your own editor working, you need to create a control generator and register in the PropertyBoundControlGenerator static class. Also, it is obvious that the ControlGenerator will create a control, which must be a Control and also IPropertyBoundControl.

What does the IPropertyBoundControlGenerator really do? It must tell which type of properties it is capable of generating controls for, tell if it is capable of generating an editor for sub-classes of these types (if they can be inherited, of course) and, finally, for creating the control.

For example:

using System;
using System.Collections.ObjectModel;
using System.Reflection;

namespace Pfz.Databasing.Controls.Generators
{
  internal sealed class StringControlGenerator:
    IPropertyBoundControlGenerator
  {
    private static readonly ReadOnlyCollection<Type> fForTypes = new ReadOnlyCollection<Type>
    (
      new Type[]
      {
        typeof(string)
      }
    );
    public ReadOnlyCollection<Type> ForTypes
    {
      get
      {
        return fForTypes;
      }
    }
    public bool CanGenerateForSubTypes
    {
      get
      {
        return false;
      }
    }
    public IPropertyBoundControl Generate(Type recordType, 
           PropertyInfo propertyInfo, string displayName)
    {
      IPropertyBoundControl result = new TextBoxBoundControl(propertyInfo);
      
      if (displayName != null)
        return new LabellerControl(result, displayName);
      
      return result;
    }
  }
}

The preceding class generates the editor for strings only. Do not generate an editor for its sub-classes (as they don't exist). Also, during the generation of the control, as the control itself does not display its DisplayName, a LabellerControl is created for it, which will add the DisplayName at the top/left of it. The generator is easy, isn't?

Well, now let's understand the control itself:

using System.Reflection;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;

namespace Pfz.Databasing.Controls.Generators
{
  internal sealed class TextBoxBoundControl:
    TextBox,
    IPropertyBoundControl
  {
    internal TextBoxBoundControl(PropertyInfo propertyInfo)
    {
      RecordProperty = propertyInfo;
      ReadOnly = true;
    }
    
    public PropertyInfo RecordProperty { get; private set; }

    private IRecord fRecord;
    public IRecord Record
    {
      get
      {
        return fRecord;
      }
      set
      {
        fRecord = value;
        
        if (value == null)
        {
          ReadOnly = true;
          return;
        }
          
        ReadRecord();
        ReadOnly = value.GetRecordMode() == RecordMode.ReadOnly;
      }
    }
    public void ReadRecord()
    {
      Text = (string)RecordProperty.GetValue(Record, null);
    }
    public void WriteRecord()
    {
      string text = Text;
      if (text == "")
        text = null;
        
      RecordProperty.SetValue(Record, text, null);
    }

    string IPropertyBoundControl.DisplayName
    {
      get
      {
        return null;
      }
    }

    protected override void OnValidating(CancelEventArgs e)
    {
      if (!ReadOnly)
        WriteRecord();
        
      base.OnValidating(e);
    }
  }
}

The control inherits from TextBox, as the TextBox is already capable of editing strings. The RecordProperty is stored, as it is part of the interface, and the control begins as readonly, as when it is created and not bound, it must be read-only. Getting the record is simple, but setting it must also update the ReadOnly and call the ReadRecord, to refresh the value being displayed. The ReadRecord and WriteRecord, well, set the text to the value of the property, and set the value of the property to the text, only changing the empty strings to null, as empty strings represent null. The DisplayName is get/set by the LabellerControl, so we can throw an exception or return null, we don't really need to implement it. And, to update the record as soon as the focus is gone, we implement OnValidating to call WriteRecord.

And that's almost all. The control is ready to be used. We only need one more thing: register the ControlGenerator. To do this, we call:

PropertyBoundControlGenerator.Add(new StringControlGenerator());

But, of course, this editor is already there, so you don't need to do this.

The future

Of course, I plan to create more controls, correct bugs, and specially make the grid faster, as today, it always recreates all controls when applying records, but my next step is to create a web version of the framework, so the interfaces and the business objects can be reutilized, and the creation of the forms can be as simple on the web as it is on the desktop. I even plan to create a form editor and a "form reader" for the web and the desktop, so you can create just one form that runs in both without any changes, but that's for the future.

I hope this framework is useful to you as it is to me.

License

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

Share

About the Author

Paulo Zemek
Engineer Microsoft Corporation
United States United States
I started to program computers when I was 11 years old, as a hobbist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.
 
At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do they work easier, faster and with less errors.
 
Now I just started working as a Senior Software Engineer at Microsoft.
 
Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com
 
Codeproject MVP 2012
Microsoft MVP 2013-2014

Comments and Discussions

 
QuestionI need your help please PinmembersaraAlahmadi12-May-11 12:01 
AnswerRe: I need your help please PinmemberPaulo Zemek13-May-11 9:49 
GeneralMy vote of 1 PinmemberYahia Alhami26-Apr-10 10:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141220.1 | Last Updated 7 Oct 2009
Article Copyright 2009 by Paulo Zemek
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid