工作记录--WPF自定义控件,实现一个可设置编辑模式的TextBox

本文介绍了如何在WPF中创建一个自定义控件LabelRenderTextBox,以解决产品对只读TextBox样式的需求。通过设置自定义属性,实现了在编辑和只读模式间的切换,避免了修改系统默认样式的复杂性。详细讲述了依赖属性的使用,以及如何响应模式变化以更新控件状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 背景

  因为最近在使用wpf开发桌面端应用,在查看页面需要把TextBox和Combox等控件设置为只读的。原本是个很简单的事,设置属性IsReadOnly="True"或IsEnabled="False"就可以解决了,可是产品觉得样式不是他想要的(背景是灰色的),想要实现的效果是和编辑时的样式一致,仅仅是不可编辑而已。我想这也简单啊,强制修改背景色和字体就完事了,结果发现TextBox修改背景色是可行的,但是修改后字体是灰色的,改也改不了,Combox更是连背景色都改不了。。。wtf

  于是开始了一段Google之路,发现别人说的都好复杂,大概明白了修改自带的不可编辑或只读的样式不是一件简单的事。于是我准备用一种变通的方法来解决这个问题,写个自定义控件,通过设置自定义控件的属性来显示/隐藏内部控件的方式。

2. 实现

  首先新建一个用户控件(UserControl),命名为LabelRenderTextBox,在LabelRenderTextBox.xaml文件编写以下内容 

   <!--DataContext="{Binding RelativeSource={RelativeSource self}} datacontext绑定自身会破坏binding链,而通过第一个child来binding则不会破坏"-->
  <StackPanel x:Name="panel" DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:LabelRenderTextBox}}">
        <Border BorderThickness="1" BorderBrush="#ccc" x:Name="label" Width="{Binding ElementName=panel, Path=ActualWidth}" Height="24" Visibility="Collapsed" Padding="5,3,0,0">
            <TextBlock Text="{Binding Text}" ></TextBlock>
        </Border>
        <TextBox x:Name="textbox" Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="{Binding ElementName=panel, Path=ActualWidth}" Height="24"
                 base:CustomizeProperty.Placeholder="{Binding Placeholder}" TextChanged="Textbox_TextChanged" ></TextBox>
    </StackPanel>

  如图,panel是UserControl内部最外层的元素,把这里的DataContext设为自身,就可以把内部控件的属性和cs文件中的属性进行绑定。label是默认隐藏的元素,textbox是默认显示的控件。label和textbox的Text属性都是绑定的LabelRenderTextBox的Text属性,宽度都是和最外层的panel的宽度进行绑定的,以便于使用者自己设置宽度。

  那么当我们使用LabelRenderTextBox控件进行编辑的时候,实际上还是通过TextBox来实现了,因为设置了Mode=TwoWay进行了双向绑定,UpdateSourceTrigger=PropertyChanged,所以当TextBox的Text改变时,LabelRenderTextBox的Text值也会跟着改变,所以我们可以在LabelRenderTextBox.cs文件里添加一个属性 public string Text { get; set; } ,可是当我们在外部直接修改LabelRenderTextBox的Text值如何使内部的TextBox值也跟着改变呢?这时候就需要用到wpf的依赖属性了,首先通过查看UserControl的继承关系可以发现UserControl是继承自DependencyObject的,意味着它是一个依赖对象,可以设置依赖属性。所以我们可以在LabelRenderTextBox.cs文件里添加以下代码:

       public string Text { get
            {
                return (string)this.GetValue(TextProperty);
            }
            set
            {
                this.SetValue(TextProperty, value);
            }
        }

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.RegisterAttached("Text", typeof(string), typeof(LabelRenderTextBox), new PropertyMetadata("", OnTextChanged));

        private static void OnTextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var element = obj as LabelRenderTextBox;
            if (element != null)
            {
                element.Text = e.NewValue as string;
            }
        }

  通过DependencyProperty.RegisterAttached方法注册一个Text属性,string类型的,类型为LabelRenderTextBox,PropertyMetadata的第一个参数是默认值,第二个参数是一个回调函数(当属性改变时进行回调),回调时通过Text的set方法来调用SetValue属性来设置依赖属性的值并通知与其绑定的属性(想知道如何实现的可以参考:WPF依赖对象(DependencyObject) 实现源码,理解WPF原理必读)。

  接下来,定义一个枚举类来列举出需要的编辑模式,我这里列举出4种模式,可根据自己的需求进行调整。

    public enum EditModes
    {
        //编辑模式
        Editable = 0,
        //只读模式
        ReadOnly = 1,      
        //只读模式展示,单击进入编辑模式,失去焦点后恢复只读模式
        Click = 2,
        //只读模式展示,双击进入编辑模式,失去焦点后恢复只读模式
        DoubleCLick = 3
    }

  定义了枚举之后就要使用了,和设置Text属性的方式来注册一个EditMode依赖属性,SetEditable方法传入true/false来设置textbox(编辑)和label(只读)的显示和隐藏。这样只要通过设置EditMode属性就可以完美实现产品的需求了,我真棒!

       #region EditModeProperty 
        public EditModes EditMode {
            get { return (EditModes)this.GetValue(EditModeProperty); }
            set {
                this.SetValue(EditModeProperty, value);
                if (value == EditModes.Editable)
                {
                    SetEditable(true);
                }
                else
                {
                    SetEditable(false);
                }
            }
        }

        public static readonly DependencyProperty EditModeProperty =
            DependencyProperty.RegisterAttached("EditMode", typeof(EditModes), typeof(LabelRenderTextBox), new PropertyMetadata(EditModes.Editable, OnEditModeChanged));

        private static void OnEditModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var element = obj as LabelRenderTextBox;
            if (element != null)
            {
                element.EditMode = (EditModes)e.NewValue;
            }
        }
        #endregion

        private void SetEditable(bool editable)
        {
            if (editable)
            {
                this.label.Visibility = Visibility.Collapsed;
                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值