找回密码
 立即注册
首页 业界区 业界 03 - LayoutPanels例子 - TextBox

03 - LayoutPanels例子 - TextBox

诀锺 2025-6-24 13:48:35
C# Maui暂时还没有TextBox,因为这个可以通过xaml样式实现,但是为了长期使用,自己写一个TextBox。
定义一个TextEventArgs
  1. public class TextEventArgs : EventArgs
  2. {
  3.      public string Text{ get; set; }
  4.      public TextEventArgs(string text)
  5.      {
  6.          Text = text;
  7.      }
  8. }
复制代码
PropertyManager和之前的例子一样,这里就不重复了。除非更新了新功能。
我的MauiProgram.cs中添加了自定义字体,这个在第一个MAUI 配置中说了,以后也不再重复了。自己去https://www.iconfont.cn/注册账号,添加自己喜欢的字体icon,然后下载下来覆盖项目中的IconFont.ttf
  1.                     fonts.AddFont("IconFont.ttf", "IconFont");
复制代码
1.png

 TextBox继承Border,以后所有自定义控件都继承于Border,不要用Frame了,因为这个在将来的未来会被淘汰。现在Border可以实现所有功能。
  1.     public class TextBox : Border
  2.     {
  3.         public static readonly BindableProperty TextProperty = BindableProperty.Create(
  4.             nameof(Text), typeof(string), typeof(TextBox), null,
  5.             BindingMode.TwoWay, propertyChanged: OnTextChanged);
  6.         public static readonly BindableProperty IsPasswordProperty = BindableProperty.Create(
  7.             nameof(IsPassword), typeof(bool), typeof(TextBox), false);
  8.         public static readonly BindableProperty IsMultilineProperty = BindableProperty.Create(
  9.             nameof(IsMultiline), typeof(bool), typeof(TextBox), false);
  10.         public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create(
  11.             nameof(Placeholder), typeof(string), typeof(TextBox), null);
  12.         public static readonly BindableProperty TextColorProperty = BindableProperty.Create(
  13.             nameof(TextColor), typeof(Color), typeof(TextBox), Colors.Black);
  14.         public static readonly BindableProperty TextSizeProperty = BindableProperty.Create(
  15.             nameof(TextSize), typeof(double), typeof(TextBox), 15d);
  16.         public static readonly BindableProperty IsReadOnlyProperty = BindableProperty.Create(
  17.             nameof(IsReadOnly), typeof(bool), typeof(TextBox), false,
  18.             propertyChanged: OnIsReadOnlyChanged);
  19.         public static readonly BindableProperty CharacterSpacingProperty = BindableProperty.Create(
  20.             nameof(CharacterSpacing), typeof(double), typeof(TextBox), 0d);
  21.         public static readonly BindableProperty CornerRadiusProperty =
  22.             BindableProperty.Create(nameof(CornerRadius), typeof(double), typeof(TextBox), 0d,
  23.                 propertyChanged: PropertyManager.CornerRadiusProperty);
  24.         public static readonly BindableProperty IconTextProperty =
  25.             BindableProperty.Create(nameof(IconText), typeof(string), typeof(TextBox), null);
  26.         public static readonly BindableProperty IconTextColorProperty =
  27.             BindableProperty.Create(nameof(IconTextColor), typeof(Color), typeof(TextBox), Colors.Gray);
  28.         public static readonly BindableProperty IconTextFontSizeProperty =
  29.                   BindableProperty.Create(nameof(IconTextFontSize), typeof(double), typeof(TextBox), 22d);
  30.         public static readonly BindableProperty IconFontFamilyProperty =
  31.             BindableProperty.Create(nameof(IconFontFamily), typeof(string), typeof(TextBox), "IconFont");
  32.         public event EventHandler<TextEventArgs>? ReturnPressed, EditingFinished;
  33.         public string Text
  34.         {
  35.             get => (string)GetValue(TextProperty);
  36.             set => SetValue(TextProperty, value);
  37.         }
  38.         public bool IsPassword
  39.         {
  40.             get => (bool)GetValue(IsPasswordProperty);
  41.             set => SetValue(IsPasswordProperty, value);
  42.         }
  43.         public bool IsMultiline
  44.         {
  45.             get => (bool)GetValue(IsMultilineProperty);
  46.             set => SetValue(IsMultilineProperty, value);
  47.         }
  48.         public string Placeholder
  49.         {
  50.             get => (string)GetValue(PlaceholderProperty);
  51.             set => SetValue(PlaceholderProperty, value);
  52.         }
  53.         public Color TextColor
  54.         {
  55.             get => (Color)GetValue(TextColorProperty);
  56.             set => SetValue(TextColorProperty, value);
  57.         }
  58.         public double TextSize
  59.         {
  60.             get => (double)GetValue(TextSizeProperty);
  61.             set => SetValue(TextSizeProperty, value);
  62.         }
  63.         public bool IsReadOnly
  64.         {
  65.             get => (bool)GetValue(IsReadOnlyProperty);
  66.             set => SetValue(IsReadOnlyProperty, value);
  67.         }
  68.         public double CharacterSpacing
  69.         {
  70.             get => (double)GetValue(CharacterSpacingProperty);
  71.             set => SetValue(CharacterSpacingProperty, value);
  72.         }
  73.         public double CornerRadius
  74.         {
  75.             get => (double)GetValue(CornerRadiusProperty);
  76.             set => SetValue(CornerRadiusProperty, value);
  77.         }
  78.         public string IconText
  79.         {
  80.             get => (string)GetValue(IconTextProperty);
  81.             set => SetValue(IconTextProperty, value);
  82.         }
  83.         public Color IconTextColor
  84.         {
  85.             get => (Color)GetValue(IconTextColorProperty);
  86.             set => SetValue(IconTextColorProperty, value);
  87.         }
  88.         public double IconTextFontSize
  89.         {
  90.             get => (double)GetValue(IconTextFontSizeProperty);
  91.             set => SetValue(IconTextFontSizeProperty, value);
  92.         }
  93.         public string IconFontFamily
  94.         {
  95.             get => (string)GetValue(IconFontFamilyProperty);
  96.             set => SetValue(IconFontFamilyProperty, value);
  97.         }
  98.         private static void OnTextChanged(BindableObject bindable, object oldValue, object newValue)
  99.         {
  100.             var tb = (TextBox)bindable;
  101.             var newText = newValue as string ?? string.Empty;
  102.             var oldText = oldValue as string ?? string.Empty;
  103.             if (newText.Length > oldText.Length && tb.IsMultiline)
  104.             {
  105.                 var addText = newText.Substring(oldText.Length);
  106.                 if (addText.Contains('\r') || addText.Contains('\n'))
  107.                 {
  108.                     //如果是回车换行,则触发ReturnPressed事件,安全派发到UI线程
  109.                     tb.Dispatcher.Dispatch(() => tb.ReturnPressed?.Invoke(tb, new TextEventArgs(newText)));
  110.                 }
  111.             }
  112.         }
  113.         //动态更新IsReadOnly属性
  114.         private static void OnIsReadOnlyChanged(BindableObject bindable, object oldValue, object newValue)
  115.         {
  116.             var tb = (TextBox)bindable;
  117.             if (tb.grid.Children.Count == 0)
  118.                 return;
  119.             if (tb.grid.Children[0] is InputView view)
  120.             {
  121.                 view.IsReadOnly = (bool)newValue;
  122.                 view.Background = (bool)newValue ? Colors.WhiteSmoke : Colors.Transparent;
  123.             }
  124.         }
  125.         private Grid grid;
  126.         public TextBox()
  127.         {
  128.             grid = new Grid()
  129.             {
  130.                 ColumnDefinitions = new ColumnDefinitionCollection
  131.                 {
  132.                     new ColumnDefinition { Width = GridLength.Star },
  133.                     new ColumnDefinition { Width = GridLength.Auto }
  134.                 },
  135.             };
  136.             // 设置布局
  137.             this.Content = grid;
  138.             this.StrokeThickness = 1;
  139.             this.Stroke = Colors.LightGray;
  140.             this.StrokeShape = new RoundRectangle() { CornerRadius = CornerRadius };
  141.             this.Padding = new Thickness(0);
  142.             //重载OnHandlerChanged中也可以初始化,构造函数会先于属性设置执行
  143.             //Dispatcher.Dispatch(Init)也可以初始化
  144.             this.Loaded += Init;
  145.         }
  146.         private void Init(object? sender, EventArgs e)
  147.         {
  148.             //凡是设置了propertyChanged的属性,都需要在这里手动初始化,因为Dispatcher.Dispatch/Loaded会在propertyChanged之后执行
  149.             InputView edit = IsMultiline ?
  150.                 new Editor()
  151.                 {
  152.                     Margin = new Thickness(0),
  153.                     AutoSize = EditorAutoSizeOption.TextChanges,
  154.                     VerticalTextAlignment = TextAlignment.Start,
  155.                 } :
  156.                 new Entry()
  157.                 {
  158.                     Margin = new Thickness(0),
  159.                     VerticalTextAlignment = TextAlignment.Start,           
  160.                 };
  161.             edit.IsReadOnly = IsReadOnly;
  162.             edit.Background = IsReadOnly ? Colors.WhiteSmoke : Colors.Transparent;
  163.             grid.Children.Add(edit);
  164.             edit.SetBinding(InputView.TextProperty, new Binding(nameof(Text), mode: BindingMode.TwoWay, source: this));
  165.             edit.SetBinding(InputView.PlaceholderProperty, new Binding(nameof(Placeholder), mode: BindingMode.TwoWay, source: this));
  166.             edit.SetBinding(InputView.TextColorProperty, new Binding(nameof(TextColor), mode: BindingMode.TwoWay, source: this));
  167.             edit.SetBinding(InputView.FontSizeProperty, new Binding(nameof(TextSize), mode: BindingMode.TwoWay, source: this));
  168.             edit.SetBinding(InputView.CharacterSpacingProperty, new Binding(nameof(CharacterSpacing), mode: BindingMode.TwoWay, source: this));
  169.             if (edit is Entry entry)
  170.             {
  171.                 edit.SetBinding(Entry.IsPasswordProperty, new Binding(nameof(IsPassword), mode: BindingMode.TwoWay, source: this));
  172.                 entry.Completed += (s, e) =>
  173.                 {
  174.                     ReturnPressed?.Invoke(this, new TextEventArgs(edit.Text));
  175.                     edit.Unfocus();
  176.                 };
  177.             }
  178.             edit.Unfocused += (s, e) =>
  179.             {
  180.                 EditingFinished?.Invoke(this, new TextEventArgs(edit.Text));
  181.             };
  182.             if (IsPassword)
  183.             {
  184.                 Label icon = new Label()
  185.                 {
  186.                     Text = IconText,
  187.                     FontFamily = IconFontFamily,
  188.                     FontSize = IconTextFontSize,
  189.                     TextColor = IconTextColor,
  190.                     VerticalTextAlignment = TextAlignment.Center,
  191.                     Margin = new Thickness(10, 0),
  192.                 };
  193.                 grid.Children.Add(icon);
  194.                 Grid.SetColumn(icon, 1);
  195.                 icon.SetBinding(Label.TextProperty, new Binding(nameof(IconText), mode: BindingMode.TwoWay, source: this));
  196.                 icon.SetBinding(Label.TextColorProperty, new Binding(nameof(IconTextColor), mode: BindingMode.TwoWay, source: this));
  197.                 icon.SetBinding(Label.FontSizeProperty, new Binding(nameof(IconTextFontSize), mode: BindingMode.TwoWay, source: this));
  198.                 icon.SetBinding(Label.FontFamilyProperty, new Binding(nameof(IconFontFamily), mode: BindingMode.TwoWay, source: this));
  199.                 TapGestureRecognizer tapGesture = new TapGestureRecognizer();
  200.                 tapGesture.Tapped += OnTapped;
  201.                 icon.GestureRecognizers.Add(tapGesture);
  202.             }
  203.         }
  204.         private void OnTapped(object? sender, TappedEventArgs e)
  205.         {
  206.             Grid? grid = (sender as Label)?.Parent as Grid;
  207.             if (grid == null)
  208.                 return;
  209.             if (grid.Children[0] is Entry entry)
  210.             {
  211.                 entry.IsPassword = !entry.IsPassword;
  212.             }
  213.         }
  214.     } 
复制代码
TextBox.xaml (前面的例子已经说明了如何把自定义控件,加到默认命名空间,以后也不再重复了)
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
  3.              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
  4.              x:Class="MauiViews.MauiDemos.Book._03.TextBox"
  5.              Title="TextBox" HeightRequest="400" WidthRequest="300">
  6.     <Grid RowDefinitions="auto,auto,auto, *">
  7.         <TextBox Placeholder="单行输入-只读" CornerRadius="15" IsReadOnly="True"/>
  8.         <TextBox Grid.Row="1" Placeholder="单行输入" ReturnPressed="TextBox_ReturnPressed" CornerRadius="15"/>
  9.         <TextBox Grid.Row="2" Placeholder="密码输入" IsPassword="True" ReturnPressed="TextBox_ReturnPressed"
  10.                  IconText=""/>
  11.         <TextBox Grid.Row="3" Placeholder="多行输入" IsMultiline="True" ReturnPressed="TextBox_ReturnPressed"/>
  12.     </Grid>
  13. </ContentPage>
复制代码
对应的cs代码
  1. using Shares.Utility;
  2. using System.Diagnostics;
  3. namespace MauiViews.MauiDemos.Book._03;
  4. public partial class TextBox : ContentPage
  5. {
  6.         public TextBox()
  7.         {
  8.                 InitializeComponent();
  9.         }
  10.     private void TextBox_ReturnPressed(object? sender, TextEventArgs e)
  11.     {
  12.                 Trace.WriteLine($"TextBox_ReturnPressed: {e.Text}");
  13.     }
  14. }
复制代码
运行效果
2.png

 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册