MAUI:自定义一个输入框

11

我对MAUI还不熟悉,只了解Xamarin.Forms的基础知识。

我想在MAUI中的一个Entry控件上添加底边框(以及边框厚度)。

在Xamarin.Forms中,我们需要为每个平台创建自定义控件和渲染器。

在发布这条消息之前,我在互联网上进行了研究。这涉及到处理程序,我有一种印象,即它只允许进行基本修改(更改背景颜色等)。

所有这些信息让我有点困惑,如果有人能为我解答,我将不胜感激。


看这里?https://learn.microsoft.com/zh-cn/dotnet/maui/user-interface/handlers/customize#customize-a-control-with-a-mapper - Bas H
官方文档只展示了简单的更改。请参考创建Maui地图控件以获取完整示例。另请参见https://dev59.com/O3QOtIcB2Jgan1zno94Z。 - ToolmakerSteve
5个回答

24

自定义特定控件实例展示了一个简单的自定义Entry示例,仅根据平台自定义某些属性。

我已经开始创建一个.Net Maui高级自定义Entry示例。请查看该存储库以获取到目前为止的实现。

状态:

  • 在Windows上演示下划线颜色和粗细。
  • 在Android上有下划线的开端。

限制(当前):

  • 在Windows上,需要将一些Entry 属性映射到包含的文本框中。
  • 尚未测试Entry 事件,以查看它们是否需要任何特殊处理。
  • Android下划线无法控制其粗细或颜色。
  • 没有在Windows或iOS以外的平台上实现。

如果您想进一步扩展此功能,请谷歌搜索xamarin forms customize entry renderer以获取平台特定代码的示例。希望我已经展示了足够的内容,以便了解如何/在哪里添加此类代码。


目前看来,做一个高级示例似乎比相应的Xamarin Forms“自定义渲染器”更加费力。

原因:

  • 需要适合Maui处理程序映射方案。可能只需要高级文档和示例。
  • 由于IEntryIEntryHandler所需的类型,扩展现有的EntryHandler变得困难。如何覆盖PlatformView类型?
  • 复制一些文件以避免与现有扩展名冲突,包括某些“内部”扩展名,这使得复制已有处理程序所做的操作(以进行细微更改)变得困难。然后我复制了那些文件,对它们进行了一些更改以避免与现有扩展名发生冲突。

待定:也许有一种避免遇到的复杂问题的方法。
另外,我可能复制了可以省略的代码。


下面是需要执行的步骤:

  1. 定义带有所需附加属性的MyEntry : Entry类。
  2. 定义用于呈现到本机UI对象的MyEntryHandler类。
  3. 在MauiProgram中添加处理程序。

1.定义带有所需附加属性的MyEntry : Entry类。

在此处,我们添加UnderlineColorUnderlineThickness

public class MyEntry : Entry
{
    /// <summary>
    /// Color and Thickness of bottom border.
    /// </summary>
    public static BindableProperty UnderlineColorProperty = BindableProperty.Create(
            nameof(UnderlineColor), typeof(Color), typeof(MyEntry), Colors.Black);
    public Color UnderlineColor
    {
        get => (Color)GetValue(UnderlineColorProperty);
        set => SetValue(UnderlineColorProperty, value);
    }

    public static BindableProperty UnderlineThicknessProperty = BindableProperty.Create(
            nameof(UnderlineThickness), typeof(int), typeof(MyEntry), 0);
    public int UnderlineThickness
    {
        get => (int)GetValue(UnderlineThicknessProperty);
        set => SetValue(UnderlineThicknessProperty, value);
    }

    public MyEntry()
    {
    }
}

2. 定义类MyEntryHandler以呈现本机 UI 对象。

使用partial class完成此操作。一部分是跨平台的,然后需要为您实现的每个平台添加另一部分。

在我的存储库中,找到MyEntryHandler.csWindows/MyEntryHandler.Windows.csAndroid/MyEntryHandler.Android.cs

MyEntryHandler.cs:

此文件包含 MyEntryHandler 的“Mapper”。

    // Cross-platform partial of class. See Maui repo maui\src\Core\src\Handlers\Entry\EntryHandler.cs
    public partial class MyEntryHandler : IMyEntryHandler //: EntryHandler
    {
        // static c'tor.
        static MyEntryHandler()
        {
            // TBD: Fill MyMapper here by copying from Entry.Mapper, then add custom ones defined in MyEntry?
        }

        //public static IPropertyMapper<IEntry, IEntryHandler> MyMapper => Mapper;
        public static IPropertyMapper<IEntry, MyEntryHandler> MyMapper = new PropertyMapper<IEntry, MyEntryHandler>(ViewMapper)
        {
            // From Entry.
            [nameof(IEntry.Background)] = MapBackground,
            [nameof(IEntry.CharacterSpacing)] = MapCharacterSpacing,
            [nameof(IEntry.ClearButtonVisibility)] = MapClearButtonVisibility,
            [nameof(IEntry.Font)] = MapFont,
            [nameof(IEntry.IsPassword)] = MapIsPassword,
            [nameof(IEntry.HorizontalTextAlignment)] = MapHorizontalTextAlignment,
            [nameof(IEntry.VerticalTextAlignment)] = MapVerticalTextAlignment,
            [nameof(IEntry.IsReadOnly)] = MapIsReadOnly,
            [nameof(IEntry.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled,
            [nameof(IEntry.Keyboard)] = MapKeyboard,
            [nameof(IEntry.MaxLength)] = MapMaxLength,
            [nameof(IEntry.Placeholder)] = MapPlaceholder,
            [nameof(IEntry.PlaceholderColor)] = MapPlaceholderColor,
            [nameof(IEntry.ReturnType)] = MapReturnType,
            [nameof(IEntry.Text)] = MapText,
            [nameof(IEntry.TextColor)] = MapTextColor,
            [nameof(IEntry.CursorPosition)] = MapCursorPosition,
            [nameof(IEntry.SelectionLength)] = MapSelectionLength,
            // From MyEntry
            [nameof(MyEntry.UnderlineThickness)] = MapUnderlineThickness
        };

        // TBD: What is this for? Cloned one on Entry.
        private static void MapUnderlineThickness(MyEntryHandler arg1, IEntry arg2)
        {
        }


        public MyEntryHandler() : base(MyMapper)
        {
        }

我还没有在所有平台的文件夹中创建最小的部分类。在repo的跨平台MyEntryHandler中,您会看到#if WINDOWS标记内的代码。目的是这个不需要用#if来包装。您还会看到很多注释掉的代码; 这样我可以看到每个平台上需要实现哪些方法。

MyEntryHandler.Windows.cs:

本质上是CreatePlatformView()。在Windows上,我选择将其实现为一个Border(四周都为零,除了底部) 包含一个TextBox


        protected override PlatformView CreatePlatformView()
        {
            var myentry = VirtualView as MyEntry;

            var textbox = new MauiPasswordTextBox
            {
                // From EntryHandler.
                IsObfuscationDelayed = s_shouldBeDelayed

                // TODO: pass some entry properties through to textbox?
            };

            MauiColor color = myentry != null
                    ? myentry.UnderlineColor
                    : MyEntry.UnderlineColorProperty.DefaultValue as MauiColor;
            int thickness = myentry != null
                    ? myentry.UnderlineThickness
                    : (int)MyEntry.UnderlineThicknessProperty.DefaultValue;

            var border = new Border
            {
                Child = textbox,
                BorderBrush = color.ToPlatform(),
                BorderThickness = new Thickness(0, 0, 0, thickness)
            };


            return border;
        }

Windows Handler中还有许多其他行。这些都是从Maui源代码中复制的。尚未确定是否需要其中哪些(如果有)。如果我想出了如何仅从Maui的EntryHandler继承的简单方法,那么这些就不再需要。但是当我继承时出现了type冲突。


3:在MauiProgram中添加Handler。

MauiProgram.cs

    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureMauiHandlers(handlers =>
            {
                handlers.AddHandler(typeof(MyEntry), typeof(MyEntryHandler));
            })
        ...

您会在 Maui 项目的 repo 中看到添加了其他类。这些类是从 Maui 源代码中复制而来的。

这些其他类由上面提到的类引用。

希望一旦更好地理解了这个主题,大多数其他类都会消失。


在 Windows 上,AppShell + MainPage 使用两个 MyEntry。其中一个有下划线并且有颜色。

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:myviews="clr-namespace:MauiCustomEntryHandler"
             x:Class="MauiCustomEntryHandler.MainPage">
             
    <ScrollView>
        <VerticalStackLayout 
                WidthRequest="500" HeightRequest="400"
                Spacing="25" Padding="30,0" BackgroundColor="LightBlue"
                HorizontalOptions="Center" VerticalOptions="Center">
            <Label Text="Hello, Maui!" FontSize="24" HorizontalOptions="Center" />
            <myviews:MyEntry Text="test" FontSize="20" UnderlineThickness="8"
                 UnderlineColor="Purple" BackgroundColor="HotPink" />
            <myviews:MyEntry UnderlineThickness="0" BackgroundColor="LightGray" />
        </VerticalStackLayout>
    </ScrollView>
 
</ContentPage>

enter image description here


哇,这是一个非常具体的答案,值得点赞。非常感谢! - Julien Guillot
2
这是一个非常好的回答。但是,请原谅我。难道真的没有更简单的方法吗? - Rob White
2
很好的问题!这似乎比在Xamarin Forms中更费力。也许这是采用更高性能的渲染器方法的代价,但我希望当更多人尝试自定义渲染器时,如何使这更容易将变得更清晰。我猜测Maui需要更多/更好的“辅助类”来处理与渲染器相关的复杂性。(我自己要到12月或1月才能进行重大的Maui工作。届时,我将看看将所有我的XForms自定义渲染器移植到Maui需要什么。我将深入挖掘Maui代码,提出更好的方法。) - ToolmakerSteve
@FreakyAli - 我同样对重新映射的必要性感到困惑,也同意 XF 更容易定制!我还没有花足够的时间来研究 Maui,找出更好的解决方案,并向开发人员建议。(~12月我将把我的当前客户从 XF 迁移到 Maui。)我认为我知道他们为什么走了这条路,但目前的实现不够方便。 - ToolmakerSteve
1
@ToolmakerSteve,我认为我有一种方法可以解决我们的问题,请查看我的存储库:https://github.com/FreakyAli/MAUI.FreakyControls - FreakyAli
显示剩余2条评论

4
我不喜欢为简单问题提供冗长的解决方案。Maui支持(有限的)CSS,所以这就是我所做的。
  1. create a stylesheet style.css (call it what you like) and put it in the '/Resources' I don't think the location matters that much

    .bottom-border {

     border-bottom:1px solid black;
    

    }

  2. Add an entry in App.xaml to access the stylesheet

    <Application.Resources>

     <StyleSheet Source="/Resources/style.css" />
    

    </Application.Resources>

您不能将此与现有的资源字典混合使用,但我不需要它们,只要您的CSS不太冒险,您可以做大多数事情,所以这不是问题。我不是MAUI样式的忠实粉丝,它似乎只是为了小小的收获而付出了很多努力,但这只是我的看法。请参考此处 使用层叠样式表样式应用程序

  1. ... StyleClass="bottom-border"添加到您的元素中,然后就完成了!

enter image description here


4

如果有人因为(看起来)简单的需要仅更改下划线颜色而来到此页面,请继续阅读。以下解决方案只需几行代码,适用于所有使用高亮/强调颜色的 MAUI 控件。

接受的答复似乎是一个有价值的教程,用于描述如何自定义 MAUI 控件的通用说明(这再次对应于可能过于泛化的原始问题标题)。但是,如果颜色行为是您唯一的目标,那么这将是完全不必要的。

MAUI 实现基于操作系统个性化方案。不幸的是,似乎无法在一个地方通用地覆盖它,但可以在特定于平台的层上控制它。

我最终得到的是仅修改一个文件,即./Platforms/Windows/App.xaml:

    <maui:MauiWinUIApplication.Resources>
        <ResourceDictionary>
            <Color x:Key="Primary">#500073</Color>
            <StaticResource x:Key="SystemAccentColorDark1" ResourceKey="Primary"/>
            <StaticResource x:Key="SystemAccentColorDark2" ResourceKey="Primary"/>
            <StaticResource x:Key="SystemAccentColorDark3" ResourceKey="Primary"/>
            <StaticResource x:Key="SystemAccentColorLight1" ResourceKey="Primary"/>
            <StaticResource x:Key="SystemAccentColorLight2" ResourceKey="Primary"/>
            <StaticResource x:Key="SystemAccentColorLight3" ResourceKey="Primary"/>
        </ResourceDictionary>
    </maui:MauiWinUIApplication.Resources>

这样做可以覆盖操作系统的颜色,据我最好的猜测,这将修改使用此强调颜色的给定平台上所有控件的行为。

Entry with customized underline color

尽管我的 Windows 设置显示蓝色为当前的强调色:

Windows OS Setting Colors

最初,我尝试修改通用的./Resources/Styles/Colors.xaml文件,以期在所有平台上一次性覆盖此行为,但似乎并没有成功。希望 MAUI 团队有朝一日能够实现通用的行为。


0

被接受的答案实在太复杂了。不妨像这样使用 GridFrameEntryBoxView

<Grid>
    <Frame Padding="0" />
    <Entry
        Margin="5,0,5,0"
        Placeholder="Put some text here..."
    />
    <BoxView
        HeightRequest="1"
        Color="LightGray"
        VerticalOptions="End"
        Margin="0,0,0,5"
    />
</Grid>

当然需要根据需要进行微调


0
虽然这个问题有点老了,但我觉得对一些.NET MAUI开发者来说可能还是有用的,他们需要知道在Android上有特定的颜色强调定义。Android的颜色强调定义在这个特定的资源目录中:

Android platform resource directory

这些颜色定义负责输入边框的颜色,可能还有一些其他颜色强调,特别是针对Android设备。不幸的是,这并不改变边框的厚度、半径或任何其他形状样式。通常情况下,只改变颜色对我来说就足够了。在我看来,粗边框使得输入更易于访问。
请注意:与整个项目的颜色定义相反,Android的定义是以小写字母而不是大写字母书写的。您还必须使用"name"而不是"x:Key"属性,否则可能会引发关于值转换器不起作用的异��。
所以... 而不是 <Color x:Key="Primary">#00FFFF</Color>
你将像这样定义颜色 <color name="colorPrimary">#00FFFF</color>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接