我正在尝试创建一个自定义的简单数字,使用“依赖属性”进行练习。
但是我有一些不需要的行为。
我的代码:
XAML:
<Grid Height="22">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<TextBox
x:Name="PART_TextboxEditable"
HorizontalContentAlignment="Right"
VerticalContentAlignment="Center"
Text="{Binding Value, ElementName=parent, Mode=TwoWay}"
IsEnabled="{Binding IsEditable, ElementName=parent}"
PreviewTextInput="TextBox_PreviewTextInput"
FontWeight="Normal">
</TextBox>
</Grid>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<RepeatButton
Click="IncressValueClick"
FontSize="8"
Background="#FFF6F6F6"
BorderThickness="0 1 1 1">
<RepeatButton.Content>
<Path
HorizontalAlignment="Center"
VerticalAlignment="Center"
Fill="Black"
Data="M4,0 L0,4 L8,4 z"/>
</RepeatButton.Content>
</RepeatButton>
<RepeatButton
Click="DecressValueClick"
Grid.Row="1"
FontSize="8"
BorderThickness="0 0 1 1"
Background="#FFF6F6F6">
<RepeatButton.Content>
<Path
HorizontalAlignment="Center"
VerticalAlignment="Center"
Fill="Black"
Data="M0,0 L8,0 L4,4 z"/>
</RepeatButton.Content>
</RepeatButton>
</Grid>
</Grid>
隐藏代码:
/// <summary>
/// Interação lógica para IntegerUpDown.xam
/// </summary>
public partial class IntegerUpDown : UserControl
{
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public int Minimum
{
get { return (int)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
public int? Maximum
{
get { return (int?)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
public int Increment
{
get { return (int)GetValue(IncrementProperty); }
set { SetValue(IncrementProperty, value); }
}
public static readonly DependencyProperty IncrementProperty =
DependencyProperty.Register(
"Increment",
typeof(int),
typeof(IntegerUpDown),
new PropertyMetadata(1));
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register(
"Maximum",
typeof(int?),
typeof(IntegerUpDown),
new PropertyMetadata(null, new PropertyChangedCallback(MaximumPropertyChangedCallback)));
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register(
"Minimum",
typeof(int),
typeof(IntegerUpDown),
new PropertyMetadata(0, new PropertyChangedCallback(MinimumPropertyChangedCallback)));
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value",
typeof(int),
typeof(IntegerUpDown),
new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(ValuePropertyChangedCalllback),
new CoerceValueCallback(ValuePropertyCoerceValueCallback)));
public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(
"ValueChanged",
RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<int>), typeof(IntegerUpDown));
private static void ValuePropertyChangedCalllback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
BindingExpression be = (d as IntegerUpDown).GetBindingExpression(ValueProperty);
if (be != null)
be.UpdateSource();
(d as IntegerUpDown).RaiseEvent(new RoutedPropertyChangedEventArgs<int>((int)e.OldValue, (int)e.NewValue, ValueChangedEvent));
}
private static void MinimumPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs baseValue)
{
var value = (int)baseValue.NewValue;
var obj = d as IntegerUpDown;
obj.SetCurrentValue(ValueProperty, (int)Math.Max(obj.Value, value));
}
private static void MaximumPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs baseValue)
{
var value = (int?)baseValue.NewValue;
var obj = d as IntegerUpDown;
obj.SetCurrentValue(ValueProperty, Math.Min(obj.Value, value ?? obj.Value));
}
private static object ValuePropertyCoerceValueCallback(DependencyObject d, object baseValue)
{
var value = (int)baseValue;
var obj = d as IntegerUpDown;
obj.CoerceValue(MaximumProperty);
obj.CoerceValue(MinimumProperty);
int newValue = Math.Max(obj.Minimum, Math.Min(value, obj.Maximum ?? value));
return newValue;
}
public IntegerUpDown()
{
InitializeComponent();
}
private void IncressValueClick(object sender, RoutedEventArgs e)
{
IncressValue();
}
private void DecressValueClick(object sender, RoutedEventArgs e)
{
DecressValue();
}
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = sender as TextBox;
var fullText = textBox.Text.Insert(textBox.SelectionStart, e.Text);
e.Handled = !int.TryParse(fullText, out _);
}
private void NumericUpDownPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
IntegerUpDown control = (IntegerUpDown)sender;
e.Handled = control.Focus() || e.Handled;
}
private void Parent_MouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Delta > 0)
IncressValue();
else
DecressValue();
}
private void IncressValue()
{
Value += Increment;
}
private void DecressValue()
{
Value -= Increment;
}
private void Parent_PreviewKeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Up:
IncressValue();
break;
case Key.Down:
DecressValue();
break;
default:
return;
}
}
这是一个非常简单的代码,但是没有按预期工作。我知道我做错了事,但是在这里我无法识别问题。
问题:
我正在使用该XAML进行测试:
<local:IntegerUpDown Value="{Binding Value}"
Maximum="15"
Minimum="10"
Increment="2"></local:IntegerUpDown>
<TextBlock
Foreground="White"
Text="{Binding Value}" Grid.Row="1"></TextBlock>
如下所示,我将手动设置'15151515'的值设置为TextBox
,将其命名为CoerceValue
,返回到newValue
的{{1}}是'15'因为CoerceValue
的值设置为15。我的Maximum
显示正确的值(15),但是我来自textbox
的值具有错误的值。
[
如果我按UP,我的值是15:
[
我的ViewModel
总是类似于Maximum
+ Maximum
和Increment
-Minimum
。我的意思是,当值达到Increment
时,我可以再单击一次,在Minimum
上有Minimum
-Increment
(例如8),但在{{1 }}显示ViewModel
(例如10)。
我的代码有什么问题?
答案 0 :(得分:1)
CoerceValueCallback
在设置源属性后 运行。您可以通过将依赖项属性的DefaultUpdateSourceTrigger
属性设置为UpdateSourceTrigger.Explicit
并显式设置source属性来解决此问题:
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value",
typeof(int),
typeof(IntegerUpDown),
new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(ValuePropertyChangedCalllback),
new CoerceValueCallback(ValuePropertyCoerceValueCallback))
{ DefaultUpdateSourceTrigger = UpdateSourceTrigger.Explicit });
...
private static void ValuePropertyChangedCalllback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
IntegerUpDown ctrl = (IntegerUpDown)d;
int newValue = (int)e.NewValue;
BindingExpression be = ctrl.GetBindingExpression(ValueProperty);
if (be != null && be.ResolvedSource != null && be.ParentBinding != null && be.ParentBinding.Path != null
&& !string.IsNullOrEmpty(be.ParentBinding.Path.Path))
{
var pi = be.ResolvedSource.GetType().GetProperty(be.ParentBinding.Path.Path);
if (pi != null)
pi.SetValue(be.ResolvedSource, newValue);
}
ctrl.RaiseEvent(new RoutedPropertyChangedEventArgs<int>((int)e.OldValue, newValue, ValueChangedEvent));
}
答案 1 :(得分:0)
看到您在询问滑块时,可能对它的工作原理进行解释可能会激发出另一种选择。
滑块的工作方式是存在一种更改Value的方法。只要有必要进行更改,就会调用此方法,并且它会检查最小最大值。 因此,这根本不是dp系统的一部分,并且值由处理检查的中介解耦。
仅设置依赖项属性的值会忽略此内部方法,因此不会应用最小值/最大值。
因为有一个文本框,所以不能“仅”重新模板化滑块并使用其内置行为。
您可以改用类似的模式。
添加一个内部dp来保存输入值。
让我们称之为无价。
将其绑定到文本框。
更改无价值后,请检查vs最小值/最大值并设置外部值或重置无价值。
按下重复按钮时,在设置无值和值之前,请检查是否与最小值/最大值相对。