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

TextBox继承Border,以后所有自定义控件都继承于Border,不要用Frame了,因为这个在将来的未来会被淘汰。现在Border可以实现所有功能。
public class TextBox : Border
{
public static readonly BindableProperty TextProperty = BindableProperty.Create(
nameof(Text), typeof(string), typeof(TextBox), null,
BindingMode.TwoWay, propertyChanged: OnTextChanged);
public static readonly BindableProperty IsPasswordProperty = BindableProperty.Create(
nameof(IsPassword), typeof(bool), typeof(TextBox), false);
public static readonly BindableProperty IsMultilineProperty = BindableProperty.Create(
nameof(IsMultiline), typeof(bool), typeof(TextBox), false);
public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create(
nameof(Placeholder), typeof(string), typeof(TextBox), null);
public static readonly BindableProperty TextColorProperty = BindableProperty.Create(
nameof(TextColor), typeof(Color), typeof(TextBox), Colors.Black);
public static readonly BindableProperty TextSizeProperty = BindableProperty.Create(
nameof(TextSize), typeof(double), typeof(TextBox), 15d);
public static readonly BindableProperty IsReadOnlyProperty = BindableProperty.Create(
nameof(IsReadOnly), typeof(bool), typeof(TextBox), false,
propertyChanged: OnIsReadOnlyChanged);
public static readonly BindableProperty CharacterSpacingProperty = BindableProperty.Create(
nameof(CharacterSpacing), typeof(double), typeof(TextBox), 0d);
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create(nameof(CornerRadius), typeof(double), typeof(TextBox), 0d,
propertyChanged: PropertyManager.CornerRadiusProperty);
public static readonly BindableProperty IconTextProperty =
BindableProperty.Create(nameof(IconText), typeof(string), typeof(TextBox), null);
public static readonly BindableProperty IconTextColorProperty =
BindableProperty.Create(nameof(IconTextColor), typeof(Color), typeof(TextBox), Colors.Gray);
public static readonly BindableProperty IconTextFontSizeProperty =
BindableProperty.Create(nameof(IconTextFontSize), typeof(double), typeof(TextBox), 22d);
public static readonly BindableProperty IconFontFamilyProperty =
BindableProperty.Create(nameof(IconFontFamily), typeof(string), typeof(TextBox), "IconFont"); public event EventHandler<TextEventArgs>? ReturnPressed, EditingFinished; public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public bool IsPassword
{
get => (bool)GetValue(IsPasswordProperty);
set => SetValue(IsPasswordProperty, value);
}
public bool IsMultiline
{
get => (bool)GetValue(IsMultilineProperty);
set => SetValue(IsMultilineProperty, value);
}
public string Placeholder
{
get => (string)GetValue(PlaceholderProperty);
set => SetValue(PlaceholderProperty, value);
}
public Color TextColor
{
get => (Color)GetValue(TextColorProperty);
set => SetValue(TextColorProperty, value);
}
public double TextSize
{
get => (double)GetValue(TextSizeProperty);
set => SetValue(TextSizeProperty, value);
}
public bool IsReadOnly
{
get => (bool)GetValue(IsReadOnlyProperty);
set => SetValue(IsReadOnlyProperty, value);
}
public double CharacterSpacing
{
get => (double)GetValue(CharacterSpacingProperty);
set => SetValue(CharacterSpacingProperty, value);
}
public double CornerRadius
{
get => (double)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
public string IconText
{
get => (string)GetValue(IconTextProperty);
set => SetValue(IconTextProperty, value);
}
public Color IconTextColor
{
get => (Color)GetValue(IconTextColorProperty);
set => SetValue(IconTextColorProperty, value);
}
public double IconTextFontSize
{
get => (double)GetValue(IconTextFontSizeProperty);
set => SetValue(IconTextFontSizeProperty, value);
}
public string IconFontFamily
{
get => (string)GetValue(IconFontFamilyProperty);
set => SetValue(IconFontFamilyProperty, value);
}
private static void OnTextChanged(BindableObject bindable, object oldValue, object newValue)
{
var tb = (TextBox)bindable;
var newText = newValue as string ?? string.Empty;
var oldText = oldValue as string ?? string.Empty;
if (newText.Length > oldText.Length && tb.IsMultiline)
{
var addText = newText.Substring(oldText.Length);
if (addText.Contains('\r') || addText.Contains('\n'))
{
//如果是回车换行,则触发ReturnPressed事件,安全派发到UI线程
tb.Dispatcher.Dispatch(() => tb.ReturnPressed?.Invoke(tb, new TextEventArgs(newText)));
}
}
}
//动态更新IsReadOnly属性
private static void OnIsReadOnlyChanged(BindableObject bindable, object oldValue, object newValue)
{
var tb = (TextBox)bindable;
if (tb.grid.Children.Count == 0)
return;
if (tb.grid.Children[0] is InputView view)
{
view.IsReadOnly = (bool)newValue;
view.Background = (bool)newValue ? Colors.WhiteSmoke : Colors.Transparent;
}
} private Grid grid;
public TextBox()
{
grid = new Grid()
{
ColumnDefinitions = new ColumnDefinitionCollection
{
new ColumnDefinition { Width = GridLength.Star },
new ColumnDefinition { Width = GridLength.Auto }
},
};
// 设置布局
this.Content = grid;
this.StrokeThickness = 1;
this.Stroke = Colors.LightGray;
this.StrokeShape = new RoundRectangle() { CornerRadius = CornerRadius };
this.Padding = new Thickness(0);
//重载OnHandlerChanged中也可以初始化,构造函数会先于属性设置执行
//Dispatcher.Dispatch(Init)也可以初始化
this.Loaded += Init;
} private void Init(object? sender, EventArgs e)
{
//凡是设置了propertyChanged的属性,都需要在这里手动初始化,因为Dispatcher.Dispatch/Loaded会在propertyChanged之后执行
InputView edit = IsMultiline ?
new Editor()
{
Margin = new Thickness(0),
AutoSize = EditorAutoSizeOption.TextChanges,
VerticalTextAlignment = TextAlignment.Start,
IsReadOnly = IsReadOnly,
Background = IsReadOnly ? Colors.WhiteSmoke : Colors.Transparent,
} :
new Entry()
{
Margin = new Thickness(0),
VerticalTextAlignment = TextAlignment.Start,
IsReadOnly = IsReadOnly,
Background = IsReadOnly ? Colors.WhiteSmoke : Colors.Transparent,
};
grid.Children.Add(edit);
edit.SetBinding(InputView.TextProperty, new Binding(nameof(Text), mode: BindingMode.TwoWay, source: this));
edit.SetBinding(InputView.PlaceholderProperty, new Binding(nameof(Placeholder), mode: BindingMode.TwoWay, source: this));
edit.SetBinding(InputView.TextColorProperty, new Binding(nameof(TextColor), mode: BindingMode.TwoWay, source: this));
edit.SetBinding(InputView.FontSizeProperty, new Binding(nameof(TextSize), mode: BindingMode.TwoWay, source: this));
edit.SetBinding(InputView.CharacterSpacingProperty, new Binding(nameof(CharacterSpacing), mode: BindingMode.TwoWay, source: this));
if (edit is Entry entry)
{
edit.SetBinding(Entry.IsPasswordProperty, new Binding(nameof(IsPassword), mode: BindingMode.TwoWay, source: this));
entry.Completed += (s, e) =>
{
ReturnPressed?.Invoke(this, new TextEventArgs(edit.Text));
edit.Unfocus();
};
}
edit.Unfocused += (s, e) =>
{
EditingFinished?.Invoke(this, new TextEventArgs(edit.Text));
};
if (IsPassword)
{
Label icon = new Label()
{
Text = IconText,
FontFamily = IconFontFamily,
FontSize = IconTextFontSize,
TextColor = IconTextColor,
VerticalTextAlignment = TextAlignment.Center,
Margin = new Thickness(10, 0),
};
grid.Children.Add(icon);
Grid.SetColumn(icon, 1);
icon.SetBinding(Label.TextProperty, new Binding(nameof(IconText), mode: BindingMode.TwoWay, source: this));
icon.SetBinding(Label.TextColorProperty, new Binding(nameof(IconTextColor), mode: BindingMode.TwoWay, source: this));
icon.SetBinding(Label.FontSizeProperty, new Binding(nameof(IconTextFontSize), mode: BindingMode.TwoWay, source: this));
icon.SetBinding(Label.FontFamilyProperty, new Binding(nameof(IconFontFamily), mode: BindingMode.TwoWay, source: this));
TapGestureRecognizer tapGesture = new TapGestureRecognizer();
tapGesture.Tapped += OnTapped;
icon.GestureRecognizers.Add(tapGesture);
}
} private void OnTapped(object? sender, TappedEventArgs e)
{
Grid? grid = (sender as Label)?.Parent as Grid;
if (grid == null)
return;
if (grid.Children[0] is Entry entry)
{
entry.IsPassword = !entry.IsPassword;
}
}
}
TextBox.xaml (前面的例子已经说明了如何把自定义控件,加到默认命名空间,以后也不再重复了)
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiViews.MauiDemos.Book._03.TextBox"
Title="TextBox" HeightRequest="400" WidthRequest="300">
<Grid RowDefinitions="auto,auto,auto, *">
<TextBox Placeholder="单行输入-只读" CornerRadius="15" IsReadOnly="True"/>
<TextBox Grid.Row="1" Placeholder="单行输入" ReturnPressed="TextBox_ReturnPressed" CornerRadius="15"/>
<TextBox Grid.Row="2" Placeholder="密码输入" IsPassword="True" ReturnPressed="TextBox_ReturnPressed"
IconText=""/>
<TextBox Grid.Row="3" Placeholder="多行输入" IsMultiline="True" ReturnPressed="TextBox_ReturnPressed"/>
</Grid>
</ContentPage>
对应的cs代码
using Shares.Utility;
using System.Diagnostics; namespace MauiViews.MauiDemos.Book._03; public partial class TextBox : ContentPage
{
public TextBox()
{
InitializeComponent();
} private void TextBox_ReturnPressed(object? sender, TextEventArgs e)
{
Trace.WriteLine($"TextBox_ReturnPressed: {e.Text}");
}
}
运行效果

03 - LayoutPanels例子 - TextBox的更多相关文章
- day23 03 组合的例子
day23 03 组合的例子 一.用到组合的方式,编写一个圆环,并能够计算出它的周长和面积 from math import pi # 从内置函数里面导入pi # 先定义一个圆类 class Circ ...
- WPF入门:数据绑定
上一篇我们将XAML大概做了个了解 ,这篇将继续学习WPF数据绑定的相关内容 数据源与控件的Binding Binding作为数据传送UI的通道,通过INotityPropertyChanged接口的 ...
- python 各模块
01 关于本书 02 代码约定 03 关于例子 04 如何联系我们 1 核心模块 11 介绍 111 内建函数和异常 112 操作系统接口模块 113 类型支持模块 114 正则表达式 115 语言支 ...
- c socket(续)
存在两种字节顺序:NBO与HBO 网络字节顺序NBO(Network Byte Order):按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题. 主机字节顺序(HBO,Host ...
- 编程那些事儿:如何快速地"借用"CSS
做前端开发有时候会碰到任务紧急,需要马上写好静态页的问题.比如,设计师给你扔了一个设计稿,要你在下班之前搞定.这时候你如热锅上的蚂蚁,如果自己写css的话,时间紧张,于是上网找了一下相关模板页面,找到 ...
- urls.py的配置[路由配置]
urls.py的配置[路由配置] Get请求与Post请求的方式 get请求: (1)地址栏输入url (2)<a href="请求url">点击</a> ...
- 【Python】【自动化测试】【pytest】
https://docs.pytest.org/en/latest/getting-started.html#create-your-first-test http://www.testclass.n ...
- c# 主机和网络字节序的转换 关于网络字节序和主机字节序的转换
最近使用C#进行网络开发,需要处理ISO8583报文,由于其中有些域是数值型的,于是在传输的时候涉及到了字节序的转换. 字节顺序是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有两种字节顺序, ...
- [C++][转]CPU字节序 网络序 主机序 大端小端
原帖:http://www.cnblogs.com/darktime/p/3298075.html 不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序最常见的有两种1 ...
- Python:数字的格式化输出
>>> 'The value is {:0,.2f}'.format(x) 'The value is 1,234.57' 需要将数字格式化后输出,并控制数字的位数.对齐.千位分隔符 ...
随机推荐
- vue3-webseek网页版AI问答|Vite6+DeepSeek+Arco流式ai聊天打字效果
2025 AI实战vue3+deepseek+arcoDesign仿DeepSeek/豆包网页版AI聊天助手. vue3-web-deepseek 实战网页PC版智能AI对话,基于vite6+vue3 ...
- 【Python】配置pip使用国内镜像源
配置pip使用国内镜像源 零.问题 使用pip安装插件时总是很慢,咋解决呢? 壹.解决 在桌面上你的文件夹内新建pip目录,一般路径如下:C:\Users\{$你的用户名},比如我的用户名是Minuy ...
- chatops
ChatOps是什么? ChatOps, 简单地说,这是一种方法,允许团队以聊天室的方式来协作和管理其基础结构.代码和数据的许多方面.通过使用聊天机器人和脚本,团队可以执行命令.查询信息,并将知识分发 ...
- 老生再谈 IoC
IoC,Spring的核心理念之一,确实这是一个老生常谈的东西.但是今天呢!又重新温习之后,想再说说自己对IOC的一些想法. IoC--Inversion of Control,控制反转.要想理解Io ...
- VSCode输出框中文乱码问题
vscode输出中文的时候,总是出现乱码.找了一个一劳永逸解决问题的方法,转载的,原教程地址:https://blog.csdn.net/a19990412/article/details/90270 ...
- python,循环中加入等待时间,使每一次循环后随机等待一段时间
爬虫爬取网页数据的时候,有时候因访问频率太过于规律导致被服务器发现,出现访问超时或者被封ip的情况.所以,每一轮爬取,后面加一个随时等待时间,可以减少被发现的概率 主要用到random和time库 实 ...
- 基于 OT-JSON 与 Immer 设计低代码/富文本场景的状态管理方案
在复杂应用中,例如低代码.富文本编辑器的场景下,数据结构的设计就显得非常重要,这种情况下的状态管理并非是redux.mobx等通用解决方案,而是需要针对具体场景进行定制化设计,那么在这里我们来尝试基于 ...
- 开源PDF处理工具——Ghostscript的安装和使用
1. 安装 Ghostscript Windows 下载 Ghostscript: 官网:https://www.ghostscript.com/download/gsdnld.html 选择适合你的 ...
- VitePress 集成 Twikoo 评论
Twikoo 是一个简洁.安全.免费的静态网站评论系统. 主要特点:免费搭建,部署简单,功能很完善,隐私护安全,通知发邮件,管理有内嵌,总之免费又方便 关于后端部署,大家可以看官网,或者这篇 Vite ...
- ASP.NET Core MiniAPI中 EndPoint相关
1.状态码返回之演化之路 1.1最基本的就是用Results或者TypedResults返回带有状态码的响应(可选Json响应体) app.MapGet("/fruit/{id}" ...