This is a migrated thread and some comments may be shown as answers.

Bug in RadMaskedNumericInput control?

1 Answer 153 Views
MaskedInput (Numeric, DateTime, Text, Currency)
This is a migrated thread and some comments may be shown as answers.
Tony
Top achievements
Rank 1
Tony asked on 04 Apr 2012, 08:40 PM
I've built a User Control that contains a RadMaskedNumericInputControl in it and 2 RepeatButtons, for use in a touch screen application. My problem is that putting the cursor into the RadMaskedNumberInputControl and typing does not change to value of the control. I can type all I want & it only lets me insert valid characters. But never does the Value property change.

What am I doing wrong? Here's the xaml for the control:

<UserControl x:Class="CarSystem.CustomControls.NumberSpinner"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
             xmlns:cs="clr-namespace:CarSystem.CustomControls"
             mc:Ignorable="d"
             Focusable="True"
             Loaded="UserControl_Loaded">
  
    <Grid Background="{Binding Path=GridBackground, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}" 
          Width="{Binding Path=Width, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <telerik:RadMaskedNumericInput BorderBrush="{Binding Path=BorderBrush, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}"
                                       FlowDirection="{Binding Path=FlowDirection, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}"
                                       Focusable="True"
                                       FontFamily="{Binding Path=FontFamily, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}"
                                       FontSize="{Binding Path=FontSize, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}"
                                       FontStretch="{Binding Path=FontStretch, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}"
                                       FontStyle="{Binding Path=FontStyle, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}"
                                       FontWeight="{Binding Path=FontWeight, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}"
                                       GotFocus="ValueBox_GotFocus"
                                       Grid.Column="0"
                                       HorizontalAlignment="Stretch"
                                       HorizontalContentAlignment="Right"
                                       InputBehavior="Insert"
                                       IsClearButtonVisible="False"
                                       LostFocus="ValueBox_LostFocus"
                                       Margin="5"
                                       Mask="{Binding Path=Mask, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}"
                                       Name="ValueBox"
                                       SelectionOnFocus="CaretToEnd"
                                       SpinMode="PositionAndValue"
                                       TabIndex="{Binding Path=TabIndex, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}"
                                       TextMode="PlainText"
                                       UpdateValueEvent="PropertyChanged"
                                       Value="{Binding Path=Value, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}"
                                       VerticalAlignment="Center" />
        <RepeatButton Background="{DynamicResource ButtonBackground}"
                      Click="IncrementButton_Click"
                      Focusable="False"
                      Foreground="{DynamicResource ButtonForeground}"
                      Grid.Column="1"
                      IsTabStop="False"
                      Name="IncrementButton">
            <Image>
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Setter Property="Source"
                                Value="/CustomControls;component/Resources/VolumeUpDay.png" />
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Path=TimeOfDayMode, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}"
                                         Value="NightTime">
                                <Setter Property="Source"
                                        Value="/CustomControls;component/Resources/VolumeUpNight.png" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
        </RepeatButton>
        <RepeatButton Background="{DynamicResource ButtonBackground}"
                      Click="DecrementButton_Click"
                      Focusable="False"
                      Foreground="{DynamicResource ButtonForeground}"
                      Grid.Column="2"
                      IsTabStop="False"
                      Name="DecrementButton">
            <Image>
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Setter Property="Source"
                                Value="/CustomControls;component/Resources/VolumeDnDay.png" />
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Path=TimeOfDayMode, RelativeSource={RelativeSource AncestorType={x:Type cs:NumberSpinner}}}"
                                         Value="NightTime">
                                <Setter Property="Source"
                                        Value="/CustomControls;component/Resources/VolumeDnNight.png" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
        </RepeatButton>
    </Grid>
      
</UserControl>

Here's the code behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
  
namespace CarSystem.CustomControls {
  
    public partial class NumberSpinner : UserControl {
        public static readonly DependencyProperty FocusedBackgroundProperty =
            DependencyProperty.Register( "FocusedBackground", typeof( Brush ), typeof( NumberSpinner ), new PropertyMetadata( null ) );
  
        public static readonly DependencyProperty FocusedForegroundProperty =
            DependencyProperty.Register( "FocusedForeground", typeof( Brush ), typeof( NumberSpinner ), new PropertyMetadata( null ) );
  
        public static DependencyProperty GridBackgroundProperty =
            DependencyProperty.Register( "GridBackground", typeof( Brush ), typeof( NumberSpinner ), new PropertyMetadata( Brushes.Transparent ) );
  
        public static readonly DependencyProperty IncrementProperty =
            DependencyProperty.Register( "Increment", typeof( double ), typeof( NumberSpinner ), new PropertyMetadata( 1.0 ) );
                  
        public static readonly DependencyProperty MaskProperty = 
            DependencyProperty.Register( "Mask", typeof( string ), typeof( NumberSpinner ), new PropertyMetadata( "#9.2" ) );
  
        public static readonly DependencyProperty MaximumProperty =
            DependencyProperty.Register( "Maximum", typeof( double ), typeof( NumberSpinner ), 
                                         new PropertyMetadata( double.NaN, new PropertyChangedCallback( OnMaximumInvalidated ) ) );
  
        public static readonly DependencyProperty MinimumProperty =
            DependencyProperty.Register( "Minimum", typeof( double ), typeof( NumberSpinner ),
                                         new PropertyMetadata( double.NaN, new PropertyChangedCallback( OnMinimumInvalidated ) ) );
  
        public static readonly DependencyProperty TimeOfDayModeProperty =
            DependencyProperty.Register( "TimeOfDayMode", typeof( TimesOfDay ), typeof( NumberSpinner ),
                                         new FrameworkPropertyMetadata( TimesOfDay.DayTime,
                                             FrameworkPropertyMetadataOptions.AffectsRender,
                                             new PropertyChangedCallback( OnTimeOfDayInvalidated ) ) );
  
        public static readonly DependencyProperty UnfocusedBackgroundProperty =
            DependencyProperty.Register( "UnfocusedBackground", typeof( Brush ), typeof( NumberSpinner ), new PropertyMetadata( null ) );
  
        public static readonly DependencyProperty UnfocusedForegroundProperty =
            DependencyProperty.Register( "UnfocusedForeground", typeof( Brush ), typeof( NumberSpinner ), new PropertyMetadata( null ) );
  
        public static readonly DependencyProperty ValueProperty = 
            DependencyProperty.Register( "Value", typeof( double ), typeof( NumberSpinner ), 
                                         new PropertyMetadata( 0.0, new PropertyChangedCallback( OnValueInvalidated ) ) );
  
        public Brush FocusedBackground {
            get { return (Brush) GetValue( FocusedBackgroundProperty ); }
            set { SetValue( FocusedBackgroundProperty, value ); }
        }
  
        public Brush FocusedForeground {
            get { return (Brush) GetValue( FocusedForegroundProperty ); }
            set { SetValue( FocusedForegroundProperty, value ); }
        }
  
        public Brush GridBackground {
            get { return (Brush) GetValue( GridBackgroundProperty ); }
            set { SetValue( GridBackgroundProperty, value ); }
        }
  
        public double Increment {
            get { return (double) GetValue( IncrementProperty ); }
            set { SetValue( IncrementProperty, value ); }
        }
  
        public string Mask {
            get { return (string) GetValue( MaskProperty ); }
            set { SetValue( MaskProperty, value ); }
        }
  
        public double Maximum {
            get { return (double) GetValue( MaximumProperty ); }
            set { SetValue( MaximumProperty, value ); }
        }
  
        public double Minimum {
            get { return (double) GetValue( MinimumProperty ); }
            set { SetValue( MinimumProperty, value ); }
        }
  
        public TimesOfDay TimeOfDayMode {
            get { return (TimesOfDay) GetValue( TimeOfDayModeProperty ); }
            set { SetValue( TimeOfDayModeProperty, value ); }
        }
  
        public Brush UnfocusedBackground {
            get { return (Brush) GetValue( UnfocusedBackgroundProperty ); }
            set { SetValue( UnfocusedBackgroundProperty, value ); }
        }
  
        public Brush UnfocusedForeground {
            get { return (Brush) GetValue( UnfocusedForegroundProperty ); }
            set { SetValue( UnfocusedForegroundProperty, value ); }
        }
  
        public double Value {
            get { return (double) GetValue( ValueProperty ); }
            set { SetValue( ValueProperty, value ); }
        }
  
        public NumberSpinner() {
            InitializeComponent();
        }
  
        private void DecrementButton_Click( object sender, RoutedEventArgs e ) {
            Value -= Increment;
        }
  
        private void IncrementButton_Click( object sender, RoutedEventArgs e ) {
            Value += Increment;
        }
  
        private void OnMaximumChanged( double newMaximum ) {
            if ( !double.IsNaN( newMaximum ) ) {
                if ( Value > newMaximum ) {
                    Value = newMaximum;
                }
                IncrementButton.IsEnabled = Value < newMaximum;
            }
        }
  
        private static void OnMaximumInvalidated( DependencyObject d, DependencyPropertyChangedEventArgs e ) {
            NumberSpinner spinner = d as NumberSpinner;
            spinner.OnMaximumChanged( (double) e.NewValue );
        }
        private void OnMinimumChanged( double newMinimum ) {
            if ( !double.IsNaN( newMinimum ) ) {
                if ( Value < newMinimum ) {
                    Value = newMinimum;
                }
                DecrementButton.IsEnabled = Value > newMinimum;
            }
        }
  
        private static void OnMinimumInvalidated( DependencyObject d, DependencyPropertyChangedEventArgs e ) {
            NumberSpinner spinner = d as NumberSpinner;
            spinner.OnMinimumChanged( (double) e.NewValue );
        }
  
        private void OnTimeOfDayModeChanged( TimesOfDay newTimeofDayMode ) {
            if ( FocusedBackground != null && UnfocusedBackground != null ) {
                ValueBox.Background = ValueBox.IsFocused ? FocusedBackground : UnfocusedBackground;
            }
            if ( FocusedForeground != null && UnfocusedForeground != null ) {
                ValueBox.Foreground = ValueBox.IsFocused ? FocusedForeground : UnfocusedForeground;
            }
        }
  
        private static void OnTimeOfDayInvalidated( DependencyObject d, DependencyPropertyChangedEventArgs e ) {
            NumberSpinner spinner = (NumberSpinner) d;
            spinner.OnTimeOfDayModeChanged( (TimesOfDay) e.NewValue );
        }
  
        private void OnValueChanged( double newValue ) {
            if ( ! double.IsNaN( Minimum ) ) {
                if ( newValue < Minimum ) {
                    Value = Minimum;
                }
                DecrementButton.IsEnabled = Value > Minimum;
            }
  
            if ( ! double.IsNaN( Maximum ) ) {
                if ( Value > Maximum ) {
                    Value = Maximum;
                }
  
                IncrementButton.IsEnabled = Value < Maximum;
            }
        }
  
        private static void OnValueInvalidated( DependencyObject d, DependencyPropertyChangedEventArgs e ) {
            NumberSpinner spinner = d as NumberSpinner;
            spinner.OnValueChanged( (double) e.NewValue );
        }
        private void UserControl_Loaded( object sender, RoutedEventArgs e ) {
            if ( FocusedBackground == null && UnfocusedBackground == null ) {
                ValueBox.Background = Background;
            }
  
            if ( FocusedForeground == null && UnfocusedForeground == null ) {
                ValueBox.Foreground = Foreground;
            }
        }
  
        private void ValueBox_GotFocus( object sender, RoutedEventArgs e ) {
            if ( FocusedBackground != null ) {
                ValueBox.Background = FocusedBackground;
            }
  
            if ( FocusedForeground != null ) {
                ValueBox.Foreground = FocusedForeground;
            }
        }
  
        private void ValueBox_LostFocus( object sender, RoutedEventArgs e ) {
            if ( UnfocusedBackground != null ) {
                ValueBox.Background = UnfocusedBackground;
            }
  
            if ( UnfocusedForeground != null ) {
                ValueBox.Foreground = UnfocusedForeground;
            }
        }
    }
}

1 Answer, 1 is accepted

Sort by
0
Tina Stancheva
Telerik team
answered on 09 Apr 2012, 04:50 PM
Hi Tony,

I think the issue is caused by your Minimum and Maximum implementation. I am not sure what values you've set to those properties but if for example you set a Minimum of 10, then your logic won't allow any other value input in the MaskedNumericInput control. This is because the ValueChanged event handler will be executed for every entered character in the MaskedNumericInput, so even if you try to enter 34, the event will firstly fire with newValue=3 and your validation logic will reset it to 10.

This is why it's really hard to limit the number in the MaskedNumericInput control and usually it is better to check the value once its fully entered for example on a SaveAll button or to use a validation mechanism to validate the input.

Regards,
Tina Stancheva
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

Tags
MaskedInput (Numeric, DateTime, Text, Currency)
Asked by
Tony
Top achievements
Rank 1
Answers by
Tina Stancheva
Telerik team
Share this question
or