Welcome to MSDN Blogs Sign in | Join | Help

Dealing with invalid input in NumericUpDown

 

What is the NumericUpDown control?

System.Windows.Forms.NumericUpDown is a control that lets the user input a number, or use up/down buttons to change a number that’s currently entered.  It hides the Text property it inherits from Control, showing a decimal Value property instead.

Why is the Text property hidden?

At any given time, the text that’s put in may be invalid, meaning that it doesn’t represent the value at all.  It would be common for a user to select all the text in the control and type in a new value.  The new value might be “-3”, where the value isn’t complete until the user types “3”.  Or the value might represent a year, where the valid range might be set to 1900-2200.  The user types “1997”, but the value isn’t valid until the user is done putting in the value.

The text in the control seems to update at funny times.

The NumericUpDown updates its text when it knows the user is done putting in text.  When the user moves focus off the control, it knows that the user is done and the text updates.  When the Value property is retrieved, the text also updates.  Depending on your UI, this may be a problem: users may think they’ve put in a valid value and click OK, only to find that the value they had typed was ignored and another value was used instead.

I could deal with an invalid value more effectively than that.  I want to know if the user put in an invalid value.

The most common request here is an indication of whether the text represents a valid value.  There are a bunch of variants on the theme that you might want, such as what the pending value is, what the pending value would be if it were valid, when the value is out of minimum/maximum range, etc.  This code will give an indication of whether the input value is a number that is in the minimum/maximum range, leaving the variants as an exercise.  I thought of putting some in, but you really end up with several properties that are very close in meaning, and it becomes hard to make the class make sense as a whole, so I just decided to go with the most common.

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Forms;

using System.Globalization;

using System.ComponentModel;

namespace NumericUpDownTest

{

    class ExtendedNumericUpdown : NumericUpDown

    {

        public event EventHandler HasValidValueChanged;

 

        private bool hasValidValue = false;

 

        [Browsable(false)]

        public bool HasValidValue

        {

            get

            {

                decimal value;

                if (Hexadecimal)

                {

                    int intValue;

                    if (!int.TryParse(Text, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out intValue))

                    {

                        return false;

                    }

                    value = (decimal)intValue;

                }

                else

                {

                    NumberStyles numberStyle = NumberStyles.Any & ~NumberStyles.AllowCurrencySymbol;

                    if (!decimal.TryParse(Text, numberStyle, CultureInfo.CurrentCulture, out value))

                    {

                        return false;

                    }

                }

                return value >= Minimum && value <= Maximum;

            }

        }

 

        protected override void OnTextChanged(EventArgs e)

        {

            base.OnTextChanged(e);

            bool newHasValidValue = HasValidValue;

            if (newHasValidValue != hasValidValue)

            {

                hasValidValue = newHasValidValue;

                OnHasValidValueChanged();

            }

        }

 

        protected virtual void OnHasValidValueChanged()

        {

            if (HasValidValueChanged != null)

            {

                HasValidValueChanged(this, EventArgs.Empty);

            }

        }

    }

}

Posted by scoberry | 0 Comments

Gotchas For Working With Windows Forms/WPF Interop

As a dev who worked on Windows Forms/WPF interop (FKA Crossbow), I compiled a list of some of the issues you might hit when working with Windows Forms and WPF together.  It's not a complete list, but it's a start.  Some of the issues are rough edges where the two technologies just don't come together smoothly.  Others are either design decisions or design differences between Windows Forms and WPF.

 

Running with WPF Doesn’t Make Windows Forms WPF

Windows Forms has a number of limitations that WPF does not.  Adding a Windows Forms control to a WindowsFormsHost in a WPF Window doesn’t mean the Windows Forms control will be able to rotate.  The control doesn’t support it, it’s not going to happen (yes, if you try this, you’ll end up with a rotated window with an obstinately unrotated Windows Forms control). 

Hwnds

Windows Forms has a separate hwnd for each control, while WPF uses one hwnd for all its content.  A side effect of this is that a Windows Forms control hosted in WPF is effectively on top of the WPF content.  WPF content inside an ElementHost is at whatever place in the z-order the ElementHost is (that is, it’s possible to put one ElementHost on top of another, but you wouldn’t see contents from two ElementHosts that overlap combining in any way).

One Child Per Host

Both WindowsFormsHost and ElementHost have a Child property (as opposed to a collection of children).  You’d need to put the things you wanted as children in some sort of container to effectively add more than one child.  That is, you could add a Windows Forms Button and CheckBox to a Panel, then set a WindowsFormsHost’s Child to the Panel (but not add the Button and CheckBox to the WindowsFormsHost directly).

Scaling

WPF and Windows Forms have different scaling models.  We tried to make it so that scaling just works, but there are places it won’t .  For example, scaling to 0 doesn’t make sense in the Windows Forms world – if you scale to 0, then back to a non-zero value, the Windows Forms control will go to 0-size and stay there.

Adapter

Both WindowsFormsHost and ElementHost have a hidden container (“adapter”) that they use to host their content.  This is an implementation detail that doesn’t usually come up, but it will come up in some of the oddities we talk about.  For WindowsFormsHost, the adapter derives from ContainerControl.  For ElementHost, the adapter derives from DockPanel.

Nesting

Nesting WindowsFormsHost inside ElementHost and vice-versa: Interoperability between Windows Forms and WPF in either direction requires some changes in messages and/or focus.  Routing to Windows Forms and back to WPF (or vice-versa) is not supported.

Focus

Focus works differently for WPF and Windows Forms, and there were some rough edges around here that we were unable to fix.  If you have focus inside a WindowsFormsHost and either minimize/restore the form or show a modal dialog, the focus inside the WindowsFormsHost may be lost – the WindowsFormsHost still has focus, but the control inside it may not.

Validation has issues because of focus issues as well.  Validation works within a WindowsFormsHost, but doesn’t work as you tab out of the WindowsFormsHost, or between two different WindowsFormsHosts.

Property Mapping

Property mappings enable you to react to changes in fonts, colors, and other properties.  Generally, Property mappings work by listening for either *Changed events or OnPropertyChanged calls, and setting appropriate properties on either the child control or its adapter.

WindowsFormsHost.Background: the WPF world supports true transparency – if you want a StackPanel to have the same Background  as its parent, you leave it null: the StackPanel won’t paint anything, and its parent will show through.  In Windows Forms, this isn’t true: if you don’t paint anything, you don’t get anything, so you’ll see a black “hole” in your app where you want the parent to show through.  Because of how the Background works, there’s no change notification when the parent’s Background changes (your Background didn’t change, it’s still null).  This means that Background doesn’t map reliably when there are changes: we aren’t aware of the changes, so can’t react to them.  If you’re changing the Background, and want the WindowsFormsHost to keep up, you can call windowsFormsHost1.PropertyMap.Apply(“Background”).  When the mapping is applied, it sets the BackColor of the adapter if the Background was a solid opaque color.  Otherwise, it sets BackgroundImage on the adapter and the hosted control to a brush based on the System.Windows.Media.Brush.  Note that this means the background will be a snapshot of the background, not a live view.  If the BackgroundImage is set, it won’t override the hosted control’s BackgroundImage unless the hosted control’s BackgroundImage was the same as the adapter’s BackgroundImage or null.

ElementHost.BackColor/BackgroundImage/BackgroundImageLayout: when any of these background-affecting properties changes, the ElementHost snaps a picture of what its background should be and uses this as a brush.  If BackColorTransparent is set to true, this snapshot is based on the ElementHost’s parent background.  Otherwise, it’s based on the ElementHost’s background.  The snapshot includes BackColor, BackgroundImage, BackgroundImageLayout, and any paint events.  Like the WindowsFormsHost Background mapping, this is a snapshot: if your Paint handler continually changes how the form looks, the ElementHost will get out of sync.  Also note that it’s not possible to remove just one of these property mappings and have it work the way you likely want it: that is, if you remove the BackgroundImageLayout mapping, BackgroundImageLayout will still be used to create the snapshot, but the snapshot will not be updated if BackgroundImageLayoutChanged fires.  This is because the snapshot actually asks the control to paint itself rather than trying to duplicate the control painting logic.

WindowsFormsHost.Cursor: this is similar to WindowsFormsHost.Background, in that we don’t get change notification when one of our parents changes their cursor.  If Cursor is mapped, the adapter overrides the Cursor getter to check up the parent’s chain for a FrameworkElement that has Cursor set (or has ForceCursor set if ForceCursor is mapped).  If either of these mappings is tampered with at all, we act as if it’s not mapped, so even
                windowsFormsHost1.PropertyMap[“Cursor”] += delegate {Console.WriteLine(“Mapping hit!”);};

will cause us not to honor the cursor mapping (since cursor is so odd, we don’t believe anything you could reasonably do in your mapping would be compatible with what we’re doing, so we take a hands-off approach).

Layout

The design is for your hosted content to fill the WindowsFormsHost or ElementHost.  In order to do that, a number of properties are set when you set an ElementHost’s Child property…

·         Height

·         Width

·         Margin

·         VerticalAlignment

·         HorizontalAlignment

·         MaximumSize

…or a WindowsFormsHost’s Child property:

·         Margin

·         Dock

·         AutoSize

·         Location

Navigation Applications

WindowsFormsHost will recreate its controls when used in a navigation application.  This means that any content that has been typed in by the user will be lost.

Message Loop Interop

WindowsFormsHost.EnableWindowsFormsInterop: this is called by the WindowsFormsHost constructor.  It adds a message filter to the WPF message loop that calls System.Windows.Forms.Control.PreProcessMessage if a System.Windows.Forms.Control was the target of the message and translates/dispatches the message if needed.

If you show a Window on a Windows Forms message loop (System.Windows.Forms.Application.Run()), you can’t type anything unless you call ElementHost.EnableModelessKeyboardInterop.  ElementHost.EnableModelessKeyboardInterop takes a Window, and adds an IMessageFilter that re-routes key-related messages to WPF’s message pump.

Opacity/Layered Windows/AllowTransparency

Setting Opacity on a WindowsFormsHost (setting AllowsTransparency on a Window) will not work, since HwndHost doesn't support this.

Dispose

When mixing Windows Forms and WPF to make sure the ElementHost or WindowsFormsHost is disposed, or you could leak resources.  Windows Forms will dispose an ElementHost when the non-modal Form it’s on closes; WPF will dispose a WindowsFormsHost if your application shuts down.  (Really the interop-specific bit here is that you could show a WindowsFormsHost on a Window in a Windows Forms message loop and never get that your Application is shutting down.)

EnableVisualStyles

System.Windows.Forms.Application.EnableVisualStyles is called in the template for a Windows Forms application.  That is, while this isn’t called by default, if you use Visual Studio to create a project, you’ll get COMCTL6-themed controls if available (buttons, checkboxes, comboboxes, etc.)  EnableVisualStyles needs to be called before handles are created on the thread.  For instance, you could add a System.Windows.Forms.Application.EnableVisualStyles call in your Window’s constructor before InitializeComponent runs.  There’s not a built-in way to do this in XAML .

Licensed Controls

Some licensed controls show dialogs in response to handle creation, perhaps either informing the user that they need a license, or that they have 3 trial uses of the control remaining.  WindowsFormsHost derives from HwndHost, and the child control’s handle is created inside BuildWindowCore, if it’s not already.  HwndHost doesn’t allow messages to be processed during BuildWindowCore – and showing a dialog pumps messages.  In order to avoid this, you could call CreateControl on the control programmatically before setting it as the WindowsFormsHost’s Child.

Posted by scoberry | 11 Comments
 
Page view tracker