【问题标题】:How do I make a usercontrol update its own property?如何让用户控件更新自己的属性?
【发布时间】:2022-01-07 14:54:46
【问题描述】:

我的 WPF 应用程序中有一个用户控件,它基本上是一个复选框和文本框。我希望根据复选框状态禁用或启用文本框,并且该状态是可数据绑定的。

这是我的用户控件:

XAML

<UserControl x:Class="WpfApp.Views.Elements.TextCheckBox"
             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" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <CheckBox Content="Disabled"
              IsChecked="{Binding CheckBoxChecked,
                          Mode=OneWayToSource,
                          UpdateSourceTrigger=PropertyChanged,
                          RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"
              HorizontalAlignment="Center"
              VerticalAlignment="Center"
              Grid.Row="1"
              Grid.Column="1"
              Margin="5,5,8,5"/>

        <TextBox Text="{Binding TextBoxText,
                        Mode=OneWayToSource,
                        UpdateSourceTrigger=PropertyChanged,
                        RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"
                IsEnabled="{Binding TextBoxEnabled,
                             Mode=OneWay,
                             RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"
                 Grid.Row="1"
                 Grid.Column="0"
                 Height="20"/>
    </Grid>
</UserControl>

代码隐藏

using System.Windows;
using System.Windows.Controls;

namespace WpfApp.Views.Elements
{
    /// <summary>
    /// Interaction logic for TextCheckBox.xaml
    /// </summary>
    public partial class TextCheckBox : UserControl
    {
        public bool TextBoxEnabled => !CheckBoxChecked;

        public string TextBoxText
        {
            get => (string)GetValue(TextBoxTextProperty);
            set => SetValue(TextBoxTextProperty, value);
        }

        public bool CheckBoxChecked
        {
            get => (bool)GetValue(CheckBoxCheckedProperty);
            set => SetValue(CheckBoxCheckedProperty, value);
        }

        public event DependencyPropertyChangedEventHandler TextPropertyChanged;

        public static readonly DependencyProperty TextBoxTextProperty =
            DependencyProperty.Register(nameof(TextBoxText), typeof(string), typeof(TextCheckBox), new PropertyMetadata(OnAnyPropertyChanged));

        public static readonly DependencyProperty CheckBoxCheckedProperty =
            DependencyProperty.Register(nameof(CheckBoxChecked), typeof(bool), typeof(CheckBoxInput), new PropertyMetadata(OnAnyPropertyChanged));

        public TextCheckBox()
            => InitializeComponent();

        static void OnAnyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
            => (obj as TextCheckBox).OnAnyPropertyChanged(args);

        void OnAnyPropertyChanged(DependencyPropertyChangedEventArgs args)
            => TextPropertyChanged?.Invoke(this, args);
    }
}

我不知道如何告诉 TextCheckBox 在选中复选框时更新其“IsEnabled”属性。

这是我使用 TextCheckBox 并且绑定正常工作的 XAML:

<UserControl x:Class="WpfApp.Views.MyView"
             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:other="clr-namespace:WpfApp.Other" xmlns:elements="clr-namespace:WpfApp.Views.Elements"
             mc:Ignorable="d" 
             other:ViewModelLocator.AutoWireViewModel="True"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <elements:TextCheckBox
                            CheckBoxChecked="{Binding IsChecked,
                                              Mode=OneWayToSource}"
                                              
                            TextBoxText="{Binding TextContent,
                                          Mode=OneWayToSource,
                                          UpdateSourceTrigger=PropertyChanged}"/>
    </Grid>
</UserControl>

编辑:

正如Khiro所建议的,我尝试使用一个依赖属性,其值在“CheckBoxChecked”的设置器中设置。

这是更改后的代码隐藏(更改后的代码在上一行用 cmets "Change here" 注释):

using System.Windows;
using System.Windows.Controls;

namespace WpfApp.Views.Elements
{
    /// <summary>
    /// Interaction logic for TextCheckBox.xaml
    /// </summary>
    public partial class TextCheckBox : UserControl
    {
        // Change here.
        public bool TextBoxEnabled => (bool)GetValue(TextBoxEnabledProperty);

        public string TextBoxText
        {
            get => (string)GetValue(TextBoxTextProperty);
            set => SetValue(TextBoxTextProperty, value);
        }

        public bool CheckBoxChecked
        {
            get => (bool)GetValue(CheckBoxCheckedProperty);
            set
            {
                SetValue(CheckBoxCheckedProperty, value);
                // Change here.
                SetValue(TextBoxEnabledProperty, !value);
            }
        }

        public event DependencyPropertyChangedEventHandler TextPropertyChanged;

        // Change here.
        public static readonly DependencyProperty TextBoxEnabledProperty =
            DependencyProperty.Register(nameof(TextBoxEnabled), typeof(bool), typeof(CheckBoxInput), new PropertyMetadata(OnAnyPropertyChanged));

        public static readonly DependencyProperty TextBoxTextProperty =
            DependencyProperty.Register(nameof(TextBoxText), typeof(string), typeof(TextCheckBox), new PropertyMetadata(OnAnyPropertyChanged));

        public static readonly DependencyProperty CheckBoxCheckedProperty =
            DependencyProperty.Register(nameof(CheckBoxChecked), typeof(bool), typeof(CheckBoxInput), new PropertyMetadata(OnAnyPropertyChanged));

        public TextCheckBox()
            => InitializeComponent();

        static void OnAnyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
            => (obj as TextCheckBox).OnAnyPropertyChanged(args);

        void OnAnyPropertyChanged(DependencyPropertyChangedEventArgs args)
            => TextPropertyChanged?.Invoke(this, args);
    }
}

但没有任何改变。单击复选框不会禁用文本框。此外,“CheckBoxChecked”的设置器中没有命中断点。

然后我尝试了 Clemens 提供的答案,我的用户控件的 XAML 变成了(仅在此处发布了更改的部分):

<TextBox Text="{Binding TextBoxText,
                Mode=OneWayToSource,
                UpdateSourceTrigger=PropertyChanged,
                RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"
            IsEnabled="{Binding TextBoxEnabled,
                        Mode=OneWay,
                        RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"
            Grid.Row="1"
            Grid.Column="0"
            Height="20">
    <TextBox.Style>
        <Style TargetType="TextBox">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsChecked, ElementName=checkBox}" Value="True">
                    <Setter Property="IsEnabled" Value="False"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

    <CheckBox x:Name="checkBox"
            Content="Newline"
            IsChecked="{Binding CheckBoxChecked,
                        Mode=OneWayToSource,
                        UpdateSourceTrigger=PropertyChanged,
                        RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Grid.Row="1"
            Grid.Column="1"
            Margin="5,5,8,5"/>

但是,再一次,什么也没发生。 :(
我在尝试任何建议之前撤消了所有更改,因此没有两个建议都有效的情况。

【问题讨论】:

  • 问题是文本框不知道TextBoxEnabled 何时更改,因此解决方案是使其成为依赖属性(只读)并在CheckBoxChecked 的设置器中设置其值,或者只是绑定到CheckBoxChecked 并使用转换器来否定它的值。
  • @Khiro,谢谢,听起来不错,但我一定做错了什么。根据我对您的建议所做的更新答案。
  • 请注意,SetValue(TextBoxEnabledProperty, !value); 方法将不起作用,因为依赖属性 X 的 CLR 包装器不得包含除 SetValue(XProperty, value) 之外的任何其他代码。 setter 可能会被框架绕过。
  • 在所有情况下,是的。见XAML Loading and Dependency Properties
  • 在这种情况下,我想将TextBoxEnabledProperty 的设置移动到CheckBoxChecked 的属性更改处理程序将解决该问题。 @K-RUSHer 请注意,您没有以正确的方式使用只读依赖属性,因为它仍然可以在课堂外的任何地方使用SetValue 进行写入,请检查此answer 以了解正确的方式;)跨度>

标签: c# wpf user-controls


【解决方案1】:

当一个内容为“已启用”的复选框被选中时,应该启用 TextBox 似乎很奇怪。

您可能只是想反其道而行之。为 CheckBox 命名并将 TextBox 的 IsEnabled 属性绑定到 CheckBox 的 IsChecked 属性:

<CheckBox x:Name="checkBox" .../>
<TextBox IsEnabled="{Binding IsChecked, ElementName=checkBox}" .../>

如果确实需要反转逻辑,请使用适当的绑定转换器,或者使用 DataTrigger:

<TextBox ...>
    <TextBox.Style>
        <Style TargetType="TextBox">
            <Style.Triggers>
                <DataTrigger
                    Binding="{Binding IsChecked, ElementName=checkBox}"
                    Value="True">
                    <Setter Property="IsEnabled" Value="False"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

【讨论】:

  • 是的,对不起,不是“启用”应该是“禁用”,现在修复。也尝试了你的方式,但没有骰子。用我所做的更新了我的答案,也许我做错了?
  • 当你通过DataTrigger设置TextBox的IsEnabled属性时,当然不能同时绑定。删除 IsEnabled="{Binding TextBoxEnabled, ...} 分配。 TextBoxEnabled 属性是完全多余的。
  • 哦,没想到,不过既然你这么说,那就很明显了。谢谢!现在它起作用了! :)
猜你喜欢
  • 2012-11-23
  • 2022-10-02
  • 1970-01-01
  • 1970-01-01
  • 2011-02-09
  • 1970-01-01
  • 2011-04-22
  • 1970-01-01
  • 2021-05-14
相关资源
最近更新 更多