WPF 新手,它给您带来的优势是显而易见的。
但是,我正在努力解决嵌套用户控件并能够从顶层访问嵌套控件属性而无需重复代码的问题。这似乎是错误的,让我觉得我错过了一些东西......
我看过其他帖子,这是最近的在WPF中将用户控件嵌套在另一个用户控件中,但不够具体,没有答案
简单用例:
我创建了一个“仅限数字”文本框,它(顾名思义)只接受字符 [0-9.-]。但我还创建了一个属性“Value”,可用于通过双精度值获取/设置显示的值。然后我相应地绑定了该属性。
这个 NumberOnlyTextBox 然后在另一个用户控件 NumericUpDownControl 中使用...但我想公开并绑定 NumberOnlyTextBox 的“Value”属性,而不必重复 NumericUpDownControl 后面的代码。是否可以在 XAML 中执行此操作,而不是重写属性和绑定?
代码:
NumberOnlyTextBox XAML
<UserControl x:Class="PracticeWPF.UserControls.TextBoxNumberOnly"
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:PracticeWPF.UserControls"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="120">
<Grid>
<TextBox Text="{Binding ValueString, Mode=OneWay}" PreviewTextInput="TextBox_PreviewTextInput" LostFocus="TextBox_LostFocus"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Background="Transparent"
FontSize="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=FontSize}"/>
</Grid>
NumberOnlyTextBox C#
public partial class TextBoxNumberOnly : UserControl, INotifyPropertyChanged
{
private static readonly Regex _regex = new Regex("[0-9.-]+");
private double _value = 0;
private string _valueString = "";
private string _valueStringFormat = "#";
public event PropertyChangedEventHandler? PropertyChanged;
public double Value
{
get { return _value; }
set
{
_value = value;
ValueString = _value.ToString(_valueStringFormat);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
}
}
public string ValueStringFormat
{
get { return _valueStringFormat; }
set
{
_valueStringFormat = value;
ValueString = _value.ToString(_valueStringFormat);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ValueStringFormat"));
}
}
public string ValueString
{
get { return _valueString; }
private set
{
_valueString = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ValueString"));
}
}
public TextBoxNumberOnly()
{
InitializeComponent();
}
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = !IsTextAllowed(e.Text, _regex);
if (((TextBox)sender).Text.Contains(".") && (e.Text == ".")) e.Handled = true;
}
private static bool IsTextAllowed(string text, Regex regex)
{
return regex.IsMatch(text);
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(((TextBox)sender).Text))
{
((TextBox)sender).Text = "0";
}
}
}
然后是 NumericUpDownControl XAML
<UserControl x:Class="PracticeWPF.UserControls.IntegerUpDown"
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:PracticeWPF.UserControls"
mc:Ignorable="d"
d:DesignHeight="80" d:DesignWidth="120">
<Grid Background="Wheat">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<local:TextBoxNumberOnly x:Name="tbnoValueDisplay" Grid.Column="0"/>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Name="UpButton" Grid.Row="0" Background="Green" Click="UpButton_Click"/>
<Button Name="DownButton" Grid.Row="1" Background="Red" Click="DownButton_Click"/>
</Grid>
</Grid>
最后是 NumericUpDownControl C#
public partial class IntegerUpDown : UserControl, INotifyPropertyChanged
{
private double _value;
private string _valueStringFormat = "#";
private string _valueString = "";
public double Value
{
get { return _value; }
set { _value = value;
ValueString = _value.ToString(_valueStringFormat);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
}
}
public string ValueStringFormat
{
get { return _valueStringFormat; }
set { _valueStringFormat = value;
ValueString = _value.ToString(_valueStringFormat);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ValueStringFormat"));
}
}
public string ValueString
{
get { return _valueString; }
private set
{
_valueString = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ValueString"));
}
}
public IntegerUpDown()
{
DataContext = this;
InitializeComponent();
}
public event PropertyChangedEventHandler? PropertyChanged;
private void UpButton_Click(object sender, RoutedEventArgs e)
{
Value++;
}
private void DownButton_Click(object sender, RoutedEventArgs e)
{
Value--;
}
}
所以问题是...
我的经验告诉我我做错了什么,但我不知道如何解决它! 非常感谢任何帮助。
为什么我要在 NumericUpDownControl 中重复 TextBoxNumberOnly 的属性?
因为
NumericUpDownControl
“包裹”(或者更确切地说隐藏)TextBoxNumberOnly
控件,即后者是前者的实现细节,并且无法直接与TextBoxNumberOnly
中的“隐藏”
NumericUpDownControl
进行交互除非你以某种方式暴露它。
是否有更简单的编写方法可以避免代码重复?我可以在 NumericUpDownControl 的 XAML 中公开 TextBoxNumberOnly 的属性吗?
不,不在 XAML 中。如果希望能够通过在 XAML 标记中设置或绑定外部控件的属性来设置内部控件的属性,则应在外部控件类中定义“包装器”依赖属性,然后将内部控件属性绑定到外部控制属性,例如:
<local:InnerControl Name="UpButton"
Value="{Binding ValueOfOuter,
RelativeSource={RelativeSource AncestorType=UserControl}}" ... />
...
<local:OuterControl ValueOfOuter="{Binding ViewModelProperty}" ... />
1234dfefd f ef d as s df sdafsdfe f sdaf d f esf e