绑定到自定义控件上的依赖项属性时遇到问题。
这是我的自定义控件。它基本上是一个文本框,只允许数字输入,并公开了一个“Value”依赖属性,您应该能够绑定到该属性以获取和设置值。
NumberBox.xaml
<UserControl x:Class="CustomControls.NumberBox"
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"
mc:Ignorable="d"
Name="root">
<TextBox Text="{Binding ValueAsString, ElementName=root, UpdateSourceTrigger=PropertyChanged}" HorizontalContentAlignment="Right"/>
</UserControl>
字符串
NumberBox.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace CustomControls
{
public partial class NumberBox : UserControl
{
public string ValueAsString
{
get { return (string)GetValue(ValueAsStringProperty); }
set { SetValue(ValueAsStringProperty, value); }
}
public static readonly DependencyProperty ValueAsStringProperty =
DependencyProperty.Register("ValueAsString", typeof(string), typeof(NumberBox), new PropertyMetadata("0", InputValidation));
public double Value
{
get => (double)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(NumberBox), new PropertyMetadata(0.0, ValueChanged));
public NumberBox()
{
InitializeComponent();
}
private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is NumberBox box)
{
var input = box.Value.ToString();
input = input.Replace(',', '.');
input = RemoveDuplicateDecimalSymbols(input);
input = RemoveLeadingZeros(input);
box.ValueAsString = input;
}
}
private static void InputValidation(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is NumberBox box)
{
var input = box.ValueAsString;
input = input.Replace(',', '.');
input = RemoveDuplicateDecimalSymbols(input);
input = RemoveLeadingZeros(input);
if (double.TryParse(input,
System.Globalization.NumberStyles.Number,
System.Globalization.CultureInfo.InvariantCulture,
out double parsed))
{
box.Value = parsed;
}
box.ValueAsString = input;
}
}
private static string RemoveDuplicateDecimalSymbols(string input)
{
var split = input.Split('.');
if (split.Length == 1)
return input;
var retval = string.Empty;
for (int i = 0; i < split.Length; i++)
{
var part = split[i];
retval += part;
if (i == 0)
retval += ".";
}
return retval;
}
private static string RemoveLeadingZeros(string input)
{
string returnValue = string.Empty;
bool allLeadingZerosRemoved = false;
for (int i = 0; i < input.Length; i++)
{
char c = input[i];
if (allLeadingZerosRemoved || c != '0')
{
returnValue += c;
if (char.IsNumber(c) || (i < input.Length - 1 && input[input.Length - 1] == '.'))
allLeadingZerosRemoved = true;
continue;
}
if (c != '0')
returnValue += c;
}
return returnValue;
}
}
}
型
然后我还做了一个小的视图模型来简化我的用例。NumberBoxViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace CustomControls
{
public class NumberBoxViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
private double someBoundValue;
public double SomeBoundValue
{
get => someBoundValue;
set
{
if (SetField(ref someBoundValue, value))
{
int i = 0; // For the sake of setting a breakpoint here
}
}
}
}
}
型
然后我在主窗口中这样使用它:MainWindow.xaml
<Window x:Class="CustomControls.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:local="clr-namespace:CustomControls"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid x:Name="MainGrid">
<local:NumberBox x:Name="Box" Value="{Binding SomeBoundValue}"/>
</Grid>
</Window>
型
MainWindow.xaml.cs
using System.Windows;
namespace CustomControls
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
NumberBoxViewModel viewModel = new NumberBoxViewModel();
Box.DataContext = viewModel;
//MainGrid.DataContext = viewModel; // Does not work either
}
}
}
型
当我在文本框中输入一些东西时,我的断点在NumberBox.InputValidation & NumberBox.ValueChanged中命中。我绑定到“Value”的属性从未触发值更改(请参阅NumberBoxViewModel.SomeBoundValue的set属性)。
我是不是错过了什么愚蠢的东西?这里发生了什么。有人能解释一下属性和依赖属性之间的绑定是如何工作的吗?用户定义的依赖属性是否与内置属性(如TextBlock上的Text字段)有不同的行为?
1条答案
按热度按时间flvlnr441#
谢谢大家的回答。这个问题是通过调整依赖项属性定义来使用双向绑定来解决的。
字符串