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

WPF Control Factory

, 20 Apr 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
This article explains some advantages and disadvantages of factories, and shows one to use for generating WPF Controls.

Introduction

In programming, factories are classes or methods used to create instances of other objects. This is useful when the method calling the factory does not know which is the best object to do what it's needed.

For example, when I hand place a control to edit strings, I use a TextBox. When I hand place a control to edit booleans, I use a checkbox. But, if I do it dynamically, like getting all properties of an object to create the controls, how do I choose the best control to show and edit the value? The answer is simple, I use a factory.

A factory can be as simple as:

public static Control CreateControl(Type dataType)
{
  if (dataType == typeof(string))
    return new TextBox();

  if (dataType == typeof(bool))
    return new CheckBox();

  return null;
}

In this case, if I need to support a new data-type, I will add new ifs to the CreateControl method. This can work for a program, but will not work if the factory is in a library called by a program with custom data-types. In this case, the program will need some way to register its editors. Using a dictionary will work but, again, it will not be complete. I could create a control that's able to edit any sub-class of a given class and, so, using a common dictionary will not work. Ok, I have many options here. I could create events, create a specialized dictionary capable of finding base types or even a combination of these. In fact, my solution is the TypeDictionary I created, capable of finding the best control given its data-type, finding its base types and even finding controls for generic types. But, to be honest, this is not the real purpose of the article, so I will move forward.

Advantages

Standardization: If the factory is used everywhere, it is guaranteed that a given data-type will always use the right editor.

Future updates: If, in the future, a new control for a given type is created, a single change in the registered controls will be enough to update the entire system.

Dynamic editors: If you simply want to create a control capable of editing all the properties of any given object, it is really easy to do so when you already have the factory. You simply create a control for each one of the properties in the object.

Maybe there are more advantages, but those three, for me, are enough.
But, there is always something that could be lost, so, let's see the disadvantages.

Disadvantages 

Lack of customization: When a factory is used, there is always some lack of customization, as the most basic control does not have all the properties of the instantiated controls.

Unalignment: When you first put a control in a window, you know its real size. You can set it to use the most appropriate size needed. But, if in the future a new control, bigger or smaller, is registered, everything can mess up.

So, how do we solve the disadvantages?

The alignment problem is automatically solved by WPF. To be honest, that's why I use WPF. The fact it allow styles and templates is an advantage, but it's not my main goal.

And for the lack of customization, I propose a weak-enforcement and Data-type wrappers.

Weak-enforcement: If a window needs special control placement, special properties set or special events captured, then don't use a factory. The factory is better suited for general purpose content, where it's better to have a fast way to show the content than to have the best way.

Data-type wrappers: In general, people are stuck with basic data-types, many times doing things like this when some validation is needed:

[Range(0, 100)]
public int PercentualValue { get; set; }

[EmailValidation]
public string Email { get; set; }

or, even worse, simple:

public int PercentualValue { get; set; }
public string Email { get; set; }

And then the validation is done on the form that presents the data. But, if instead, a custom type is used, we can have:

public Range0_100 PercentualValue { get; set; }
public EmailString Email { get; set; }

And, not only the Range0_100 and EmailString can already do their validations in the constructor, but a factory can use a better suited control for editing Range0_100 and EmailString than the controls used to edit ints and strings in general.

After some time working with custom data-types for validations and specialized editors, I think everyone will love them. But, even this has some problems. ORMs in general don't support custom data-types. Regarding this, I can only say that my own ORM framework was, in fact, the base of this article. In future, I will re-publish my Database framework, which will use this WpfControlFactory as its heart for Desktop controls.

Attached is a sample that uses the Pfz.WpfControls, which uses the factory model, and a very simple program, without any styles or visual appeal, that simply shows some records, and even has a specialized control for YesNo values, showing a radio-group instead of a combobox.

The TypeDictionary

I already explained some advantages and disadvantages of factories and presented some possible solutions to the disadvantages. Now, I will explain a little about my factory implementation.

My factory is based in the TypeDictionary generic class. This class is, in fact, composed of 6 dictionaries.

  • One to register values (factories) that only work on exact matches;
  • One to register values for a class-type or any sub-type;
  • One to register values for an interface-type or any class that implements such interface;
  • And the same three dictionaries for generic-type definitions (like List<>).

As the type of the value is generic, such type-dictionary can be used for anything that requires some "class hierarchy" identification but, of course, I use it for factories.

The real important method is: TryFindUp.

The method is a little hard to read, but I will try to explain the steps:

  • First, we try an exact match.
  • So, if the type is a generic type definition, we look in the ExactGeneric dictionary. If the type is not a generic type definition, we look into the Exact dictionary.
  • Obviously, if a match is found we return it. But, if not, we continue.
  • If the type is an interface, we try to find a value in the interfaces dictionary.
  • Then, if the type is a class, we try to find a value for InheritableTypes, for the actual type and for each one of it's base types.
  • Finally, we don't care if the given type was a class or an interface. We extract all the interfaces, try to order them (so, if a class has interface C and D, one that inherits from A and one that inherits from B, we will see C and D first [they will not have a guaranteed order] and A and B after).

  • And, again, we will try to find a value for each one of the interfaces.
  • We will only return that no value is found after finishing those steps unsuccessfully.

That's it

I hope this article helps anyone wanting to create a dynamic or general purpose Windows/object editors.

History

  • 16th April, 2010: Initial version
  • 20th April, 2010: More detailed article and using routed-events properly

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

 
GeneralObject oriented debate PinmemberJason McBurney20-Apr-10 6:39 
Of course we could build a type RangedInt0_100, but really what value does that buy us? Why trade the complexity of managing another type in the system for the minimal benefits that a simple check could provide? While I agree there is a object-oriented-benefit to the new type approach, mostly reuse etc., the business case seems to favor the simpler implementation, the simple check; therefore, this level of oop does not seem valuable in a large system. Of course the argument changes significantly if one alters the type in question and the number of system wide uses.
 
Your thoughts?
You can only be young once. But you can always be immature.
- Dave Barry

GeneralRe: Object oriented debate [modified] PinmemberPaulo Zemek20-Apr-10 8:57 
GeneralToo short PinmemberShahriar Iqbal Chowdhury17-Apr-10 10:36 
GeneralRe: Too short PinmemberPaulo Zemek17-Apr-10 10:38 

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 | Mobile
Web04 | 2.8.141022.2 | Last Updated 20 Apr 2010
Article Copyright 2010 by Paulo Zemek
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid