WPF嵌套用户控件,公开下级控件属性

pgvzfuti  于 2023-03-04  发布在  其他
关注(0)|答案(1)|浏览(174)

对WPF来说是新的,它给你的优势是显而易见的。
然而,我在嵌套用户控件和从顶层访问嵌套控件属性而不重复代码方面遇到了困难。这似乎是错误的,让我觉得我错过了一些东西...
我看过其他帖子,这是最近的Nest a user control inside another user control in WPF,但不够具体,也没有答案

    • 简单使用案例:**

我创建了一个"Number Only" TextBox,它(顾名思义)只接受字符[0 - 9. -]。但我也创建了一个属性"Value",它可以通过一个双精度值来获取/设置显示的值。然后我相应地绑定了该属性。
此NumberOnlyTextBox随后用于另一个用户控件NumericUpDownControl ...但我希望公开和绑定NumberOnlyTextBox的"Value"属性,而不必重复NumericUpDownControl的代码。是否可以在XAML中完成此操作,而不必重写属性和绑定?

    • 密码:**

仅数字文本框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>

仅数字文本框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--;
    }
}

"所以问题是..."
1.为什么在NumericUpDownControl中重复TextBoxNumberOnly的属性?
1.有没有一种更简单的方法可以避免代码重复?我可以只在NumericUpDownControl的XAML中公开TextBoxNumberOnly的属性吗?
我的经验告诉我我做错了什么,但我不知道如何解决它!任何帮助非常感谢。

62o28rlo

62o28rlo1#

为什么在NumericUpDownControl中重复TextBoxNumberOnly的属性?
因为NumericUpDownControl“ Package ”(或者说隐藏)了TextBoxNumberOnly控件,也就是说,后者是前者的实现细节,没有办法直接从NumericUpDownControl与“隐藏”的TextBoxNumberOnly交互,除非您以某种方式将其公开。
有没有一种更简单的方法可以避免代码重复?我可以只在NumericUpDownControl的XAML中公开TextBoxNumberOnly的属性吗?
否,不在XAML中。如果希望能够通过在XAML标记中设置或绑定外部控件的属性来设置内部控件的属性,则应在外部控件类中定义“ Package ”依赖项属性,然后将内部控件属性绑定到外部控件属性,例如:

<local:InnerControl Name="UpButton"
                    Value="{Binding ValueOfOuter,
                        RelativeSource={RelativeSource AncestorType=UserControl}}" ... />
...
<local:OuterControl ValueOfOuter="{Binding ViewModelProperty}" ... />

相关问题