Я пытаюсь создать небольшой POC для многоразового элемента управления, но у меня наблюдается странное поведение, и я, ради бога, не могу понять, почему.
Поэтому я надеюсь, что кто-нибудь здесь сможет пролить свет на это.
Я пытаюсь создать многоразовый числовой ввод с кнопками +/- и настраиваемыми минимальными/максимальными значениями.
Если задан мин/макс и пользователь вводит значение, большее или меньшее, мне нужен элемент управления, чтобы обновить связанное свойство до минимального или максимального допустимого значения.
Все работает нормально при вводе значения вручную, но не работает, когда привязанное свойство задается с помощью кода (например, при нажатии кнопки в основной модели представления)
Это мой вывод, который я получаю, когда вручную заполняю слишком большое значение:
Setting BindThis to 105
BindThis set to 105
UC OnPropertyChanged: 25 -> 105
UC forcing min/max value: 100)
Setting BindThis to 100
BindThis set to 100
UC OnPropertyChanged: 105 -> 100
И вот что я получаю, когда устанавливаю для BindThis значение при нажатии кнопки:
Setting BindThis to 1000,2345678
UC OnPropertyChanged: 100 -> 1000,2345678
UC forcing min/max value: 100)
UC OnPropertyChanged: 1000,2345678 -> 100
BindThis set to 1000,2345678
Обратите внимание, как объект DependencyProperty пользовательского элемента управления вызывается во время установки свойства модели представления, а не все это делается после установки свойства модели представления при ручном заполнении значения. >
Обратите внимание, что я использую CommunityToolkit.Mvvm, а приложение работает под управлением .NET 8:
Хотя я сомневаюсь, что это каким-либо образом повлияет на то, что я испытываю.
Учитывая следующий UserControl:
namespace WpfApp1
{
///
/// Interaction logic for UserControl1.xaml
///
public partial class UserControl1 : UserControl
{
public UserControl1()
{
_increaseCommand = new RelayCommand(OnIncrease, CanIncrease);
_decreaseCommand = new RelayCommand(OnDecrease, CanDecrease);
InitializeComponent();
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
nameof(Value), typeof(double?), typeof(UserControl1),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ValueProperty_OnPropertyChanged));
// https://stackoverflow.com/questions/726 ... the-source
private static void ValueProperty_OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine($"UC OnPropertyChanged: {e.OldValue} -> {e.NewValue}");
UserControl1 instance = (UserControl1)d;
double? newValue = (double?)e.NewValue;
double? result = newValue;
if(newValue != null)
{
if(newValue < instance.Min)
{
result = instance.Min;
}
else if(newValue > instance.Max)
{
result = instance.Max;
}
}
if(newValue != result)
{
Debug.WriteLine($"UC forcing min/max value: {result})");
d.SetCurrentValue(ValueProperty, result);
}
instance.Increase.NotifyCanExecuteChanged();
instance.Decrease.NotifyCanExecuteChanged();
}
public double? Value
{
get => (double?)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public double Min { get; set; }
public double Max { get; set; }
private RelayCommand _increaseCommand;
public RelayCommand Increase => _increaseCommand;
private RelayCommand _decreaseCommand;
public RelayCommand Decrease => _decreaseCommand;
private bool CanDecrease() => Value - 1 >= Min;
private void OnDecrease() => Value -= 1;
private bool CanIncrease() => Value + 1 Value += 1;
}
}
Что используется как таковое:
namespace WpfApp1
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
public class MainWindowViewModel : ObservableObject
{
private double? _value;
public double? BindThis
{
get => _value;
set
{
Debug.WriteLine($"Setting BindThis to {value}");
SetProperty(ref _value, value);
Debug.WriteLine($"BindThis set to {_value}");
}
}
private void OnButtonMinClick() => BindThis = -1000.12345;
private void OnButtonClick() => BindThis = 1000.2345678;
private RelayCommand _btnClick;
public ICommand ButtonClick => _btnClick;
private RelayCommand _btnMinClick;
public ICommand ButtonMinClick => _btnMinClick;
public MainWindowViewModel()
{
_btnClick = new RelayCommand(OnButtonClick);
_btnMinClick = new RelayCommand(OnButtonMinClick);
}
}
}
Обновление 1:
Как предложено в комментариях, мне следует использовать CoarceValueCallback и не использовать SetCurrentValue в OnPropertyChanged .
Однако это приводит к одинаковому поведению по всей линии. В свойстве модели представления сохраняется неправильное значение, а в текстовом поле отображается ограниченное значение.
Вот изменения:
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
nameof(Value), typeof(double?), typeof(UserControl1),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ValueProperty_OnPropertyChanged, ValueProperty_OnCoarceValue));
private static object ValueProperty_OnCoarceValue(DependencyObject d, object baseValue)
{
UserControl1 instance = (UserControl1)d;
double? newValue = (double?)baseValue;
double? result = newValue;
if (newValue != null)
{
if (newValue < instance.Min)
{
result = instance.Min;
}
else if (newValue > instance.Max)
{
result = instance.Max;
}
}
return result;
}
// https://stackoverflow.com/questions/726 ... the-source
// https://blog.scottlogic.com/2012/02/06/ ... light.html
private static void ValueProperty_OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//Debug.WriteLine($"UC OnPropertyChanged: {e.OldValue} -> {e.NewValue}");
UserControl1 instance = (UserControl1)d;
//double? newValue = (double?)e.NewValue;
//double? result = newValue;
//if(newValue != null)
//{
// if(newValue < instance.Min)
// {
// result = instance.Min;
// }
// else if(newValue > instance.Max)
// {
// result = instance.Max;
// }
//}
//if(newValue != result)
//{
// Debug.WriteLine($"UC forcing min/max value: {result})");
// d.Dispatcher.BeginInvoke(() => d.SetCurrentValue(ValueProperty, result));
//}
instance.Increase.NotifyCanExecuteChanged();
instance.Decrease.NotifyCanExecuteChanged();
}
Подробнее здесь: https://stackoverflow.com/questions/790 ... cyproperty
Странное поведение с повторно используемым элементом управления и DependencyProperty ⇐ C#
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение
-
-
Странное поведение с повторно используемым элементом управления и DependencyProperty
Anonymous » » в форуме C# - 0 Ответы
- 17 Просмотры
-
Последнее сообщение Anonymous
-
-
-
Странное поведение с повторно используемым элементом управления и DependencyProperty
Anonymous » » в форуме C# - 0 Ответы
- 16 Просмотры
-
Последнее сообщение Anonymous
-
-
-
Как реализовать элемент управления WPF с собственным DataTemplate DependencyProperty?
Anonymous » » в форуме C# - 0 Ответы
- 22 Просмотры
-
Последнее сообщение Anonymous
-