1

Based on this https://stackoverflow.com/a/31831596/5723199 I try resolve my problem.

But when I run my code and try decrement value in User Control text box has good value, but in Main Window label, which has binding to property in User Control show wrong - "-90" instead of 0

NumericUpDown.xaml:

<UserControl x:Class="TestKontrolkiNumeriUpDown.NumericUpDown.NumericUpDown"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:TestKontrolkiNumeriUpDown.NumericUpDown"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <TextBox x:Name="txtBoxValue"  Grid.Row="0" Grid.Column="0" Grid.RowSpan="3"/>
        <Button x:Name="btnUp" Content="Up" Grid.Row="0" Grid.Column="1" Click="BtnUp_Click"/>
        <Button x:Name="btnDown" Content="Down" Grid.Row="2" Grid.Column="1" Click="BtnDown_Click"/>
    </Grid>
</UserControl>

NumericUpDown.xaml.cs

  public partial class NumericUpDown : UserControl
{
    public double MinValue { get; set; } = 5D;

    public double Value
    {
        get => (double)GetValue(ValueProperty);
        set => SetValue(ValueProperty, value);
    }

    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double),typeof(NumericUpDown), 
        new FrameworkPropertyMetadata(
            0D, 
            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
            OnMyPropertyChanged,
            OnCoerceValue,
            false,
            UpdateSourceTrigger.Explicit
            ));

    private static object OnCoerceValue(DependencyObject d, object baseValue)
    {
        NumericUpDown nud = d as NumericUpDown;

        if (((double)baseValue) < nud.MinValue)
        {
            //OnMyPropertyChanged
            return nud.MinValue;
        }
        return baseValue;
    }
    private static void OnMyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {

        NumericUpDown numericUpDown = d as NumericUpDown;

        if (numericUpDown == null)
            return;

        BindingExpression bindingExpression = numericUpDown.GetBindingExpression(NumericUpDown.ValueProperty);
        if (bindingExpression != null)
            bindingExpression.UpdateSource();

        ((NumericUpDown)d).txtBoxValue.Text = ((double)e.NewValue).ToString();
    }

    public NumericUpDown()
    {
        InitializeComponent();
    }


    private void BtnUp_Click(object sender, RoutedEventArgs e)
    {
        Value += 100;
    }

    private void BtnDown_Click(object sender, RoutedEventArgs e)
    {
        Value -= 100;
    }



}

MainWindow.xaml

<Window x:Class="TestKontrolkiNumeriUpDown.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:nudCtrl="clr-namespace:TestKontrolkiNumeriUpDown.NumericUpDown"
    xmlns:thisPage="clr-namespace"
    xmlns:local="clr-namespace:TestKontrolkiNumeriUpDown"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<StackPanel>
    <Label Content="{Binding Rabat}"/>
    <nudCtrl:NumericUpDown Value="{Binding Rabat, Mode=TwoWay}" Margin="20"/>
</StackPanel>

MainWindow.xaml.cs

   public partial class MainWindow : Window, INotifyPropertyChanged
{
    private double _rabat;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Rabat
    {
        get { return _rabat; }
        set {
            _rabat = value;
            OnPropertyChanged();
        }
    }


    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;

        Rabat = 10D;
    }

    public void OnPropertyChanged([CallerMemberName] string propName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }
}

Github: https://github.com/promwand00/NumericUpdownProblem

promwand
  • 11
  • 2
  • Is there a particular reason to set `UpdateSourceTrigger.Explicit`? Did you debug your code to check if `bindingExpression.UpdateSource();` is actually called? – Clemens Nov 24 '19 at 21:10
  • Because the way doesn't work without it. This is described in the link I gave (in the main question). But that solution still doesn't work as it should in a particular case. In the example I gave in the OnCoerceValue question, it looks like it is called before OnMyPropertyChanged but the value before correction is still passed to the Main Window. (https://giphy.com/gifs/LNkJLJc2WoUh3Mt0wD) I must have missed something, or I'm doing wrong? – promwand Nov 25 '19 at 07:50

1 Answers1

0

You had to look to the accepted answer in the post you have refered to.

When you set a value on a dependency property, binding expressions are updated before value coercion runs

For me it looks like a bug in a framework. Why you don't want to put the value correction to the PropertyChangedCallback? Such a way, I think, it should work as expected, or?

private static void OnMyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{

    var numericUpDown = d as NumericUpDown;

    if (numericUpDown == null)
        return;
    if (((double)e.NewValue) < numericUpDown.MinValue)
    {
        numericUpDown.Value = numericUpDown.MinValue;
        return;
    }
    BindingExpression bindingExpression = numericUpDown.GetBindingExpression(NumericUpDown.ValueProperty);
    if (bindingExpression != null)
        bindingExpression.UpdateSource();

    ((NumericUpDown)d).txtBoxValue.Text = ((double)e.NewValue).ToString();
}

Don't forget to remove the CoerceValueCallback.

Rekshino
  • 6,954
  • 2
  • 19
  • 44