Bug in RadMaskedNumericInput control?

2 posts, 0 answers
  1. Tony
    Tony avatar
    111 posts
    Member since:
    May 2011

    Posted 04 Apr 2012 Link to this post

    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;
                }
            }
        }
    }
  2. Tina Stancheva
    Admin
    Tina Stancheva avatar
    3298 posts

    Posted 09 Apr 2012 Link to this post

    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 >>

  3. UI for WPF is Visual Studio 2017 Ready
Back to Top