This project has moved. For the latest updates, please go here.

Created a ByteUpDown from IntegerUpDown but it does not work as custom editor

Jun 13, 2012 at 11:25 PM
Edited Jun 14, 2012 at 1:04 PM

Hello,

We needed to use updowns with Byte values so I created a ByteUpDown as below. I also added the ParseByte method to the NumericUpDown class. Everything compiles cleanly. However, when I attempt to use my new class as a custom editor, no updown is displayed, the editor appears like a blank textbox. If I change my data to int and use the IntegerUpDown instead, everything works fine. My custom editor xaml setup is below the ByteUpDown class definition.

protected static byte ParseByte(string text, IFormatProvider cultureInfo)
{
    return Byte.Parse(text, NumberStyles.Any, cultureInfo);
}
using System;
using System.Windows;

namespace Xceed.Wpf.Toolkit
{
    public class ByteUpDown : NumericUpDown<byte?>
    {
        #region Constructors

        static ByteUpDown()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof( ByteUpDown ), new FrameworkPropertyMetadata( typeof( ByteUpDown ) ) );
            DefaultValueProperty.OverrideMetadata(typeof( ByteUpDown ), new FrameworkPropertyMetadata( default( byte ) ) );
            IncrementProperty.OverrideMetadata(typeof( ByteUpDown ), new FrameworkPropertyMetadata( (byte)1 ) );
            MaximumProperty.OverrideMetadata(typeof( ByteUpDown ), new FrameworkPropertyMetadata( byte.MaxValue ) );
            MinimumProperty.OverrideMetadata(typeof( ByteUpDown ), new FrameworkPropertyMetadata( byte.MinValue ) );
        }

        #endregion //Constructors

        #region Events

        // Must be defined as object. See comment in UpDownBase attached event for explanation.
        public static readonly new RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<object>), typeof(ByteUpDown));
        // Not needed because base class implements this functionality.
        //public new event RoutedPropertyChangedEventHandler<object> ValueChanged
        //{
        //    add
        //    {
        //        AddHandler(ValueChangedEvent, value);
        //    }
        //    remove
        //    {
        //        RemoveHandler(ValueChangedEvent, value);
        //    }
        //}

        #endregion //Events

        #region Base Class Overrides

        protected override void OnValueChanged(byte? oldValue, byte? newValue)
        {
            // Must call base version first.
            base.OnValueChanged(oldValue, newValue);

            // Event args must be of type object to match attached event definition.
            RoutedPropertyChangedEventArgs<object> args = new RoutedPropertyChangedEventArgs<object>(oldValue, newValue);
            args.RoutedEvent = ByteUpDown.ValueChangedEvent;
            RaiseEvent(args);
        }

        protected override byte? CoerceValue(byte? value)
        {
            if (value < Minimum)
                return Minimum;
            else if (value > Maximum)
                return Maximum;
            else
                return value;
        }

        protected override void OnIncrement()
        {
            if (Value.HasValue)
                Value += Increment;
            else
                Value = DefaultValue;
        }

        protected override void OnDecrement()
        {
            if (Value.HasValue)
                Value -= Increment;
            else
                Value = DefaultValue;
        }

        protected override byte? ConvertTextToValue(string text)
        {
            byte? result = null;

            if (String.IsNullOrEmpty(text))
                return result;

            try
            {
                //don't know why someone would format a byte as %, but just in case they do.
                result = FormatString.Contains("P") ? Decimal.ToByte(ParsePercent(text, CultureInfo)) : ParseByte(text, CultureInfo);
                result = CoerceValue(result);
            }
            catch
            {
                Text = ConvertValueToText();
                return Value;
            }

            return result;
        }

        protected override string ConvertValueToText()
        {
            if (Value == null)
                return string.Empty;

            return Value.Value.ToString(FormatString, CultureInfo);
        }

        protected override void SetValidSpinDirection()
        {
            ValidSpinDirections validDirections = ValidSpinDirections.None;

            if (Value < Maximum || !Value.HasValue)
                validDirections = validDirections | ValidSpinDirections.Increase;

            if (Value > Minimum || !Value.HasValue)
                validDirections = validDirections | ValidSpinDirections.Decrease;

            if (Spinner != null)
                Spinner.ValidSpinDirection = validDirections;
        }

        protected override void ValidateValue(byte? value)
        {
            if (value < Minimum)
                Value = Minimum;
            else if (value > Maximum)
                Value = Maximum;
        }

        #endregion //Base Class Overrides
    }
}

Here is my custom editor xaml: 
<xctk:PropertyGrid Grid.Column="2" SelectedObject="{Binding}" AutoGenerateProperties="False">
    <xctk:PropertyGrid.EditorDefinitions>
        <xctk:EditorDefinition>
            <xctk:EditorDefinition.PropertiesDefinitions>
                <xctk:PropertyDefinition Name="Rows" />
                <xctk:PropertyDefinition Name="Columns" />
            </xctk:EditorDefinition.PropertiesDefinitions>
            <xctk:EditorDefinition.EditorTemplate>
                <DataTemplate>
                    <xctk:ByteUpDown Value="{Binding Value}" Minimum="1" Maximum="50" DefaultValue="1" />
                </DataTemplate>
            </xctk:EditorDefinition.EditorTemplate>
        </xctk:EditorDefinition>
    </xctk:PropertyGrid.EditorDefinitions>
</xctk:PropertyGrid>

Any ideas and/or suggestions would be appreciated.
Coordinator
Jun 15, 2012 at 7:40 PM

You almost make it work.  You forgot to add the style in the NumericUpDown/Themes/Generic.xaml.

 

Here is the style you'll probably want to use:

<Style TargetType="{x:Type local:ByteUpDown}" BasedOn="{StaticResource NumericUpDown}">
   <Setter Property="TextAlignment" Value="Right" />
   <Setter Property="WatermarkTemplate" Value="{StaticResource DefaultWatermarkTemplate}" />
</Style>
Jun 18, 2012 at 3:00 PM

Many thanks!