LOGIN    REGISTER    YOUR CART

homepage
VisualHint's blog
May10

How to setup a truly dynamic combobox in the PropertyGrid?

SPG offers some great ways to build dynamic properties. But often this is a mix of declarative code and dynamic calls to the SPG API to alter the content or the behavior of the grid. However in some cases a complete dynamic solution is needed. For example, some classes may be generated by external tools and it’s impossible or not desired to decorate their properties with attributes. In other contexts, enumerations are not welcome because their content is too ... hardcoded. This article proposes a way to setup a property based on a single integer identifier and a collection of strings to display in the combobox. To make it a little more complex, our combobox will be the unit of another numerical property.

The first thing we need is a new TypeConverter to instruct the grid that we have a set of values to display in a combobox. The two involved methods to override are GetStandardValuesSupported() and GetStandardValues(). Of course we will derive our converter from the Int32Converter. Here is the code:

public class MyIntConverter : Int32Converter
{
    public override bool GetStandardValuesSupported(
        ITypeDescriptorContext context)
    {
        return true;
    }

    public override TypeConverter.StandardValuesCollection
        GetStandardValues(ITypeDescriptorContext context)
    {
        return new StandardValuesCollection( ??? );
    }
}

Notice the question marks? This is where you pass the allowed identifiers for your list. There are several ways to get a dynamic list from the TypeConverter. For example you could use the passed context (which in SPG is of type PropertyTypeDescriptorContext) to get the list from the target instance. Here we will create a new attribute that will contain the initial list of integers. The definition of this attribute could be:

public class AllowedIntsAttribute : Attribute
{
    public int[] AllowedInts;

    public AllowedIntsAttribute(params int[] allowedInts)
    {
        AllowedInts = allowedInts;
    }
}

GetStandardValues can then be written like this:

public override TypeConverter.StandardValuesCollection
    GetStandardValues(ITypeDescriptorContext context)
{
    PropertyTypeDescriptorContext c = (PropertyTypeDescriptorContext)context;
    AllowedIntsAttribute attr = (AllowedIntsAttribute)c.OwnerPropertyEnumerator.Property.
        GetValue(PropertyUnitLook.UnitValue).GetAttribute(typeof(AllowedIntsAttribute));
    return new StandardValuesCollection(attr.AllowedInts);
}

And here is how all is connected when the grid is filled:

propEnum = AppendManagedProperty(rootEnum, _id++, "Width", typeof(int),
    2, "");
propEnum.Property.Look = new PropertyUnitLook();
propEnum.Property.Feel =
    GetRegisteredFeel(PropertyGrid.FeelEditUnit);

propEnum.Property.AddManagedValue(PropertyUnitLook.UnitValue, typeof(int),
    10, new AllowedIntsAttribute(1,2),
    new TypeConverterAttribute(typeof(MyIntConverter)),
    new PropertyValueDisplayedAsAttribute(new string[2]
        { "feet", "meters" }));

If the list of identifiers and the list of displayed strings need to be modified, this is possible in SPG. Attributes set dynamically can be accessed with PropertyValue.GetAttribute() and modified if their API enable to do so.

I hope you will appreciate this tutorial. If something is unclear or you need more explanations, please leave a comment.

May09

How to set a null value to a DateTimePicker without any compromise?

Just to make it clear that this is the first concern about the MS DateTimePicker, here is a list of famous links:

These articles all try various tricks to create a nullable DateTimePicker. Unfortunately, while the intentions are good, it does always create secondary issues (look into the comments). SFPE will naturally support null values but from a user point of view the path to set null can still be debated. There are two questions:

  1. How does the user clear the control and hence puts null as the value?
  2. How does the user inputs a new non null value when the current value is null?

SFPE is flexible so a lot of techniques can be implemented. But before writing here the result of my experiments (I don’t want to influence your ideas), I would like to hear from you in the comments what would be your perfect solution. Don’t limit yourself to what the MS DateTimePicker could do. Forget about it! Just try to imagine the best answers for the two above questions. This “perfect solution” could then be kept for the default mechanism in SFPE.

May06

How to format a number with a specific CultureInfo/NumberFormatInfo in the PropertyGrid ?

“How to format a number in the PropertyGrid” is a question that I’m often asked. The following answer is valid for the MS PropertyGrid and for Smart PropertyGrid.Net. Therefore it is not in the SPG documentation.

The following explanation will be given for a Double number. You can easily apply it to a Single or a Decimal. If your target instance has a Double property, it uses by default the DoubleConverter TypeConverter. Unfortunately (for the present need) this converter uses the “R” format specifier when it converts from a double to a string in the ConvertTo method and the NumberStyles.Float style when it converts back a string to a double in the ConvertFrom method. “R” is called the round-trip format and it does not use the culture set for the application. It’s useful to ensure that a double converted to a string will result in the same original double when converted back.

It means that to solve our problem we must define a new TypeConverter and attach it to our Double property. We can derive it from DoubleConverter but we will override the ConvertTo and ConvertFrom methods. Here is the code of this converter:

public class MyConverter : DoubleConverter
{
    public override object ConvertTo(ITypeDescriptorContext context,
        CultureInfo culture, object value, Type destinationType)
    {
        if ((destinationType == typeof(string)) &&
            (value.GetType() == typeof(double)))
        {
            double num = (double)value;
            return num.ToString("N", culture.NumberFormat);
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context,
        CultureInfo culture, object value)
    {
        if (value is string)
            return double.Parse(value as string, culture);

        return base.ConvertFrom(context, culture, value);
    }
}

To set a specific culture usable by the PropertyGrid, you can change the global culture of the application. For example:

Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-us"); 

In SPG.Net you have also another option. You can set a CultureInfo instance per property. If you don’t, it will use the one of the application. This is useful for example to display numbers with various precisions or different currency formats.

protected override void OnPropertyCreated(PropertyCreatedEventArgs e)
{
    PropertyValue propertyValue = e.PropertyEnum.Property.Value;

    if ((propertyValue != null) && (propertyValue.UnderlyingType ==
        typeof(double)))
    {
        e.PropertyEnum.Property.Value.CultureInfo.NumberFormat.
            NumberDecimalDigits = 6;
    }

    base.OnPropertyCreated(e);
}
May02

The ten deceptions of the DateTimePicker

If you are here reading these lines it’s because you have something against the .Net DateTimePicker. And you’re not alone since, if you do a search in the newsgroups, the same questions keep coming: “how do I set the background color of the DTP?”, or “it does not accept a null value and my DateTime column in my database has some empty cells”.

CodeProject and other similar developer communities give you a lot of pseudo solutions to solve one or two issues of the control… when they don’t create new problems. They start with deriving from the DateTimePicker class and overriding certain methods and end with the magic textbox displayed in front to hide the date value when null is the only thing desired. So it’s obvious that everyone complains about the DateTimePicker shortcomings and everyone needs a solution. What are the problems of this component which is, don’t forget it, the underlying win32 SysDateTimePick32 window class, nothing more:

  1. The complaint number one is that it doesn’t accept a null or DBNull value. This is critical when binding it to a nullable DateTime column in a database. Consequently it’s not possible to display a custom string when this value is null.
  2. It suffers some display issues: when closing the calendar, the focus cue disappears, except if a checkbox is visible, in which case this is the one taking the focus. Speaking of the checkbox, it will sometimes show this cue even when the control itself is not focused. No mouse hover effect is seen on the dropdown button and checkbox, however there is one for the updown button. And don’t ask me why you can’t show an updown button with a dropdown button at the same time…
  3. There are also some inconsistencies compared to the usual combobox and checkbox controls. When clicking those buttons, the corresponding action is triggered on mouse down instead of being triggered on mouse up.
  4. If you disable the date by clicking on the checkbox, which is by the way ridiculously small under a non xp theme, clicking the dropdown button instantly reenables it.
  5. There is no text formatting options except for the month calendar. The background color can’t be set.
  6. Programmatically you can’t control which field is the selected one.
  7. This is the same for the dropdown calendar. You won’t be able to show it or hide it without the user intervention.
  8. Some people would like a way to automatically jump to the next editable field when all characters have been typed in the current field instead of having to type the right arrow key each time.
  9. Where are callback fields? Do you remember the DTNFORMATQUERY and DTNFORMAT messages? It was great to create custom fields but this feature is not natively present in the .net control.
  10. No custom culture can be applied on the displayed string. The Microsoft KB says it clearly: you can do it only by changing the current culture in the control panel. Pretty annoying for applications that want to change this regional setting on the fly, usually when choosing a language in a preferences box.

There are some more issues that I had, specifically in Smart PropertyGrid.Net. Among the built-in inplace controls this was the only one to vertically align the text at the top. No way to change that. When a property expands on multiple rows or simply increases its vertical margin this is not very nice. Also, a field calculates its width based on the largest string it can accept (“September” for the month for example). This was also an issue with SPG. When the inplace control was not active, I had no way to display the text with the same spacing between fields and therefore when selecting the datetime property, all the fields were shifting in a non pleasant way.

I guess there are some more concerns that you encountered while coping with the DateTimePicker. You can add them in the comments so that we have a good starting list to know what else needs to be fixed.

Apr30

The perfect DateTimePicker? Not only!

It is well known that a good product is one that provides a solution to your own problem, and as a result would meet the needs of a wider audience. Some time ago I discovered a problem in SPG.Net. I couldn’t make a true DateTimePicker inplace control achieving the level of quality of the overall product. The win32 control from Microsoft has so many issues and inconsistencies that it was a nightmare trying to work around them. Finally I decided to remove this inplace control from SPG and announced it to my customers. I was really dissatisfied by this decision but I had no other choice. Time passed and I was still sure there was nothing I could do about it. I then came to the conclusion that I could rewrite a DateTime picker from scratch but at first I was discouraged because I didn’t know where to start.

This is quite typical. Let time go by and one day you find the courage. I began slowly, one small thing at a time and sooner than expected I had a base control to work with. Instead of tying it to datetime specific requirements, I made the effort of designing it as a generic editor able to edit a series of rule-based fields, a rule being for example “an integer in a specific range”. Smart FieldPackEditor was born but it didn’t know it yet.

I was whispering to myself “it would be great if it could be a standalone product but I guess that many component vendors, and especially the big ones, have already covered this domain”. Guess what? No! I found absolutely no “field editor” controls and concerning DateTimePickers I found only two but one seems to emphasize only a vista look and feel, while the other one was so basic that I can’t even remember who offered it. All the other possibilities are in the “free” domain, mainly on CodeProject. They all derive from a DateTimePicker or a TextBox and try to do their best to fix one or several issues of the MS DTP but the result is never the perfect solution.

So, in essence what are the goals of SFPE:

  • To be the best (if not unique right now) DateTimePicker replacement of choice for the old MS DTP.
  • To be a generic framework able to let a user edit a set of fields where each field is governed by a rule.
  • To be extended via a derived class so that it is fully usable in SPG.Net and therefore will fix my initial problem.

I need your input to make this product an absolute must. So I have opened a new section in the forums and a mailing list on the homepage (so that you can be informed when the beta begins). Given what I wrote above, what do you think ? What are your features of choice ? What are the issues you want fixed first?

In a next post, I will try to summarize all the problems of the MS DTP and expose how SFPE fixes them.

Apr29

A new home for VisualHint

VisualHint has reached another milestone. Today, I’m launching my new web site that offers the GUI components that I design. The redesign was not out of boredom, but simply the result of a need to support more than one product. The previous site had been designed around a main product (Smart PropertyGrid) and the accompanying news section and I had no idea how to add another product which would have the same importance. Therefore, it was time for a redesign.

I took Xara Xtreme Pro, a very nice tool to quickly output vector drawings and began coding css and html pages. I must admit it was quite long for me because I’m not a web designer, but I like the process of being fully involved in the construction of my new home. That being said, I hope you will like it, as much as I do. Btw, here is a list of what changes were made during the redesign:

  • The previous blog was more a “news section” than a blog. I will now try to maintain a true blog discussing PropertyGrids, DateTimePickers and custom controls in general.
  • Comments and trackbacks are open for more communication with visitors and customers.
  • The homepage clearly presents the products, the latest announcement and the latest blog post.
  • The navigation is easier than before.

So now you would like to know more about this new product, is that it? To be followed on the next post…

Jan24

Value validation in great form

So far, value validation in SPG was simple and clean: no annoying popup but also the obligation to see the previous value automatically restored.

For the next release, value validation is being reworked. It will still offer the existing behavior but also a mode called KeepFocus. Under this new mode, the focus stays on the faulty value until the user types a valid value (this is what happens in the Microsoft PropertyGrid but unfortunately spoiled with the modal popup). Inside a new validation callback the client application will be given the choice to display a visual clue to indicate to the end-user that there is a problem. The best option will be to show the free Skybound VisualTip.

Here are two examples grabbed from my current build. In the first situation, an event is sent to the client application and indicates that a TypeConverter was not able to convert the value set by the user. The text of the exception is passed with the event and the client application made the choice to display it in a customized tooltip.

TypeConverter: invalid value

In the second situation, the user entered a value out of the range set by an attached PropertyValidatorMinMax class. The validator proposed a generic text but the client application made the choice to display a custom text.

Property Validator: invalid value

I must add that the visual capabilities of VisualTips are endless. Very nice product. Marry it with Smart PropertyGrid.Net and you succeed bringing the right experience to your end-users.

Jan02

PropertyValueChanged for collections bug fix

I was not aware of this bug in the Microsoft PropertyGrid until a customer told me it was doing the same thing in Smart PropertyGrid. This is interesting enough to add a post here about it. The issue is that PropertyValueChanged is not fired for certain collections. It works with an array but doesn’t for a List<> for instance. The reason is that the UITypeEditor.EditValue(...) method returns the reference on the original collection even if its content has been modified. I looked in Reflector and after this method returns, the Microsoft code does a simple “if (obj1 != obj2) ...” which of course returns false and doesn’t trigger the event. My code was actually doing the same thing, so I fixed it to handle this particular case. This is another reason to purchase from a small company that reacts and makes a product that evolves fast. This enhancement is available in version 2.1 beta2 that is out today.

Oct17

A website for PropertyGrid resources

I launched today a new web site, minimalist in page count, intended to .Net developers who use the Microsoft PropertyGrid (and secondarily Smart PropertyGrid.Net since it understands all the framework classes related to Type discovery). Because this is sometimes difficult to get the right information for developers who start using this complex component, it seems it was essential to have a well known page referencing all famous and less famous articles, forum posts and other various materials. I don’t want to spend time on developing the web site itself, but surely want to enrich its content with my findings and your submissions. Even nice tips and tricks can fit there. The address is www.propertygridresourcelist.com.

Sep21

Navigate with the TAB key in a PropertyGrid

Navigate the PropertyGrid with the TAB key This feature is one of the most discussed about PropertyGrids in general, whether they are mfc, activeX or .net controls from third party developers. The model provided by Microsoft does not offer the possibility to navigate from value to value with the TAB key, or shift+TAB for reverse order and this is a lack that annoys a lot of people. One of my customers had a long discussion with me about this feature. He wanted absolutely this kind of navigation to help his end-user edit a long list of mandatory variables. Using Return and Escape plus the arrow keys was not enough to provide a good experience to his own customers. Since that time, several other visitors and customers asked me the same feature. After visiting some developer forums and articles, I was convinced that browsing property values with the TAB key would be a must for Smart PropertyGrid.Net. So here is what I came with:

  • You are now able to choose between two major modes. In the first one you need to validate or cancel the current edition of the value and use the arrow key to go to the next editable property. In the second one , you navigate to the next inplace control with TAB and to the previous with shift+TAB. There are also several non-exclusive options for all tastes:

  • The "TabKeyWithAutoFocus" option will focus inside an inplace control as soon as you click on a property label. This is a great way to minimize the actions required by the end-user.

  • The "TabKeyInSubControls" option will allow to access child components in inplace controls. For example, for a trackbar feel, TAB will first go in the textbox part and another TAB will jump to the slider.

  • The "TabKeyInChildProperties" option will open any collapsed hierarchy to place the focus in the next editable property value.

  • The "TabKeySkipReadonlyProperties" option will skip readonly properties. Without it, these properties are visited so that a clipboard copy operation is possible while navigating the grid with the keyboard.

  • At last, the "TabKeyLeavesAtExtremes" option will normally tab to the remaining controls in a form once the top or bottom properties are edited. Without it, the focus will continue to rotate in the PropertyGrid.

There will be a small sample available to showcase all those modes in a few days.