博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
工作记录--WPF自定义控件,实现一个可设置编辑模式的TextBox
阅读量:7207 次
发布时间:2019-06-29

本文共 13999 字,大约阅读时间需要 46 分钟。

1. 背景

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

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

2. 实现

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

   
  

  如图,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属性来设置依赖属性的值并通知与其绑定的属性(想知道如何实现的可以参考:)。

  接下来,定义一个枚举类来列举出需要的编辑模式,我这里列举出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;                this.textbox.Visibility = Visibility.Visible;            }            else            {                this.label.Visibility = Visibility.Visible;                this.textbox.Visibility = Visibility.Collapsed;            }        }

  可是按照产品的需求只要Editable和ReadOnly两个属性就够了,其他两个是用来扩展的,可以在合适的场景下带来更好的用户体验,默认展示只读模式,单击或者双击变为编辑模式,失去焦点后恢复只读模式。实现方法也很简单,直接在UserControl定义几个事件,在对应的事件函数进行如下处理。

   private void Click(object sender, MouseButtonEventArgs e)        {            if (EditMode == EditModes.Click)                SetEditable(true);        }        private void DoubleClick(object sender, MouseButtonEventArgs e)        {            if (EditMode == EditModes.DoubleCLick)                SetEditable(true);        }        private void Lost_Focus(object sender, RoutedEventArgs e)        {            if(EditMode == EditModes.DoubleCLick || EditMode == EditModes.Click)                SetEditable(false);        }

  你可能会想,如果想监听TextBox的事件该怎么做呢,也很简单,在cs文件里定义相同的事件,然后在cs文件里监听对应事件,触发时调用定义的事件即可。

public event TextChangedEventHandler TextChanged;               private void Textbox_TextChanged(object sender, TextChangedEventArgs e)        {                       this.TextChanged?.Invoke(this, e);        }

  是不是很简单呢?希望可以帮到遇到相同问题的朋友,完整代码如下:

xaml
using APP_YFT.Base;using System;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Text;using System.Text.RegularExpressions;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Navigation;using System.Windows.Shapes;namespace YZ.HIS.UserControls.LabelRenders{    ///     /// LabelRenderTextBox.xaml 的交互逻辑    ///     public partial class LabelRenderTextBox : UserControl    {        public LabelRenderTextBox()        {            InitializeComponent();        }        #region events        public event TextChangedEventHandler TextChanged;               private void Textbox_TextChanged(object sender, TextChangedEventArgs e)        {            if (RequiredValue == RequiredValues.Number)            {                TextBox textBox = sender as TextBox;                string text = textBox.Text;                Regex reg = new Regex(@"^[0-9]*$");                if (text == null || (text.Count() == 0) || !reg.IsMatch(text))                {                    textBox.Text = "";                    return;                }            }            this.TextChanged?.Invoke(this, e);        }        //清空输入框事件        private void ExecCleanSearchText(object sender, RoutedEventArgs e)        {            Button btn = sender as Button;            if (btn == null)                return;            TextBox tb = btn.TemplatedParent as TextBox;            if (tb != null)            {                tb.Text = "";            }        }        private void Click(object sender, MouseButtonEventArgs e)        {            if (EditMode == EditModes.Click)                SetEditable(true);        }        private void DoubleClick(object sender, MouseButtonEventArgs e)        {            if (EditMode == EditModes.DoubleCLick)                SetEditable(true);        }        private void Lost_Focus(object sender, RoutedEventArgs e)        {            if(EditMode == EditModes.DoubleCLick || EditMode == EditModes.Click)                SetEditable(false);        }        #endregion        #region TextProperty         //public string Text { get; set; }        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;            }        }        #endregion        #region PlaceholderProperty         public string Placeholder { get            {                return (string)this.GetValue(PlaceholderProperty);            }            set            {                this.SetValue(PlaceholderProperty, value);            }        }        public static readonly DependencyProperty PlaceholderProperty =            DependencyProperty.RegisterAttached("Placeholder", typeof(string), typeof(LabelRenderTextBox), new PropertyMetadata("", OnPlaceholderChanged));        private static void OnPlaceholderChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)        {            var element = obj as LabelRenderTextBox;            if (element != null)            {                element.Placeholder = e.NewValue as string;            }        }        #endregion        #region RequiredValueProperty         public RequiredValues RequiredValue        {            get { return (RequiredValues)this.GetValue(RequiredValueProperty); }            set            {                this.SetValue(RequiredValueProperty, value);            }        }        public static readonly DependencyProperty RequiredValueProperty =            DependencyProperty.RegisterAttached("RequiredValue", typeof(RequiredValues), typeof(LabelRenderTextBox), new PropertyMetadata(RequiredValues.String, OnRequiredValueChanged));        private static void OnRequiredValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)        {            var element = obj as LabelRenderTextBox;            if (element != null)            {                element.RequiredValue = (RequiredValues)e.NewValue;            }        }        #endregion        #region UseSearchProperty         public bool UseSearch        {            get { return (bool)this.GetValue(UseSearchProperty); }            set            {                this.SetValue(UseSearchProperty, value);                if (value)                    this.textbox.Style = this.FindResource("mySearchTextBoxStyle") as Style;            }        }        public static readonly DependencyProperty UseSearchProperty =            DependencyProperty.RegisterAttached("UseSearch", typeof(bool), typeof(LabelRenderTextBox), new PropertyMetadata(false, OnUseSearchChanged));        private static void OnUseSearchChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)        {            var element = obj as LabelRenderTextBox;            if (element != null)            {                element.UseSearch = (bool)e.NewValue;            }        }        #endregion        #region CaretIndexProperty         public int CaretIndex {            get { return (int)this.GetValue(CaretIndexProperty); }            set            {                this.SetValue(CaretIndexProperty, value);                this.textbox.CaretIndex = value;            }        }        public static readonly DependencyProperty CaretIndexProperty =            DependencyProperty.RegisterAttached("CaretIndex", typeof(int), typeof(LabelRenderTextBox), new PropertyMetadata(0, OnCaretIndexChanged));        private static void OnCaretIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)        {            var element = obj as LabelRenderTextBox;            if (element != null)            {                element.CaretIndex = (int)e.NewValue;            }        }        #endregion        #region SelectionStartProperty         public int SelectionStart        {            get { return (int)this.GetValue(SelectionStartProperty); }            set            {                this.SetValue(SelectionStartProperty, value);                this.textbox.SelectionStart = value;            }        }        public static readonly DependencyProperty SelectionStartProperty =            DependencyProperty.RegisterAttached("SelectionStart", typeof(int), typeof(LabelRenderTextBox), new PropertyMetadata(0, OnSelectionStartChanged));        private static void OnSelectionStartChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)        {            var element = obj as LabelRenderTextBox;            if (element != null)            {                element.SelectionStart = (int)e.NewValue;            }        }        #endregion        #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;                this.textbox.Visibility = Visibility.Visible;            }            else            {                this.label.Visibility = Visibility.Visible;                this.textbox.Visibility = Visibility.Collapsed;            }        }           }    public enum EditModes    {        //编辑模式        Editable = 0,        //只读模式        ReadOnly = 1,              //只读模式展示,单击进入编辑模式,失去焦点后恢复只读模式        Click = 2,        //只读模式展示,双击进入编辑模式,失去焦点后恢复只读模式        DoubleCLick = 3    }    public enum RequiredValues    {        String = 0,        Number = 1    }}
C#

  

转载于:https://www.cnblogs.com/despacito/p/11002751.html

你可能感兴趣的文章
CLR的执行模型(4):执行程序集的代码
查看>>
同一脚本sh 脚本名 报Syntax error: "(" unexpected而./脚本名不报错,求解!!
查看>>
ZJOI2008皇帝的烦恼
查看>>
新手windows安装nginx
查看>>
浏览器兼容问题踩坑收集
查看>>
Python 实用技巧
查看>>
object c中@property 的使用
查看>>
Sping 核心IOC容器
查看>>
poj 2524
查看>>
MapReduce
查看>>
论文阅读笔记五十六:(ExtremeNet)Bottom-up Object Detection by Grouping Extreme and Center Points(CVPR2019)...
查看>>
回收期计算
查看>>
response响应
查看>>
10 个十分难得的 javascript 开发经验
查看>>
Common Subsequence
查看>>
【CSS3】标签使用说明
查看>>
n皇后问题—回溯法 C++实现
查看>>
on delete cascade
查看>>
Connected Graph
查看>>
openfile iscisi 配置
查看>>