XAML 单击按钮后激活字符验证行为

wtzytmuj  于 2023-03-06  发布在  其他
关注(0)|答案(2)|浏览(115)

如何在单击按钮后立即激活或重新评估为条目设置的CharactersValidationBehavior的结果?

<Entry.Behaviors>

    <toolkit:CharactersValidationBehavior IsValid="{Binding InputValid}"
                                          Flags="ValidateOnValueChanged"                                                         
                                          CharacterType="Alphanumeric"
                                          MinimumCharacterTypeCount="1" />
                                                
</Entry.Behaviors>

非可选条目在XAML中正确设置,并在ContentView显示后立即在ContentView的Code-Behind中的InputValid-Property中进行计算。但我真正想要的是,计算在单击按钮后立即进行。
我怎么能这么做?

a0x5cqrl

a0x5cqrl1#

点击后可以添加需要验证的按钮事件,然后定义Entry控件的焦点事件,当Entry获得焦点后,去掉验证,这样就可以达到点击激活或者重新评估的目的,我写了一个demo来测试,大家可以参考下面的代码。
Xaml文件:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             x:Class="MauiApp7.MainPage">
    <ContentPage.Resources>
        <Style x:Key="InvalidEntryStyle" TargetType="Entry" x:Name="invalidStyle">
            <Setter Property="TextColor" Value="Red" />
        </Style>
        <Style x:Key="ValidEntryStyle" TargetType="Entry" x:Name="validStyle">
            <Setter Property="TextColor" Value="Green" />
        </Style>
    </ContentPage.Resources>

    <StackLayout>
        <Entry x:Name="enrty" Focused="OnEntryFocused" >
            <Entry.Behaviors>
                <toolkit:CharactersValidationBehavior x:Name="charactersValidationBehavior"/>
            </Entry.Behaviors>
        </Entry>

        <Button Text="Evaluate" Clicked="OnEvaluated"/>

</ContentPage>

.CS文件:

using CommunityToolkit.Maui.Behaviors;

namespace MauiApp7;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    private void OnEvaluated(object sender, EventArgs e) {
        charactersValidationBehavior.InvalidStyle = invalidStyle;
        charactersValidationBehavior.ValidStyle = validStyle;
        charactersValidationBehavior.Flags = ValidationFlags.ValidateOnValueChanged;
        charactersValidationBehavior.CharacterType = CharacterType.Digit;
        charactersValidationBehavior.MinimumCharacterTypeCount = 3;  
        enrty.Behaviors.Add(charactersValidationBehavior);
    }

    private void OnEntryFocused(object sender, EventArgs e) {
        enrty.Behaviors.Remove(charactersValidationBehavior);
    }
}
pprl5pva

pprl5pva2#

我试着实现你的基本概念,但似乎它没有工作,因为我想要的。
基本上,我想做一个小小的修改,当用户在相应条目中的输入没有被正确验证时,我想显示一个图标或一个带有文本的标签。
在我的例子中,我有一个ContentPage,它使用了两个ContentView来托管条目和标签,还有一个标签,它应该以红色或类似的颜色显示错误消息。

内容页:

<?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"
             xmlns:viewModels="clr-namespace:MyApp.ViewModels"
             xmlns:controls="clr-namespace:MyApp.Pages.Views"
             x:DataType="viewModels:ManageItemsViewModel"
             x:Class="MyApp.Pages.ManageItems"
             Title="Some title">

    <VerticalStackLayout>

        <Label 
            Text="Addn Items"
            FontAttributes="Bold"
            VerticalOptions="Center" 
            HorizontalOptions="Center"
            Padding="20" />

        <StackLayout>
            <controls:MyItemInputControlView NameLabelString="Label for Input 1:"
                                             IsOptionalLabelString="Mandatory"
                                             PlaceholderString="a placeholder 1"
                                             EntryInput="{Binding Item.Name}"
                                             InputValid="{Binding NameInput1Valid, Mode=TwoWay}" />

            <controls:MyItemInputControlView NameLabelString="Label for Input 2:"
                                             IsOptionalLabelString="Optional"
                                             PlaceholderString="a placeholder 2"
                                             EntryInput="{Binding Item.Brand}"
                                             InputValid="{Binding NameInput2Valid, Mode=TwoWay}" />
            

            <StackLayout Margin="20, 50, 15, 0">
                <Grid RowSpacing="10">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>

                    <Button Grid.Row="0" Grid.Column="0" Margin="5"
                            Text="OK"
                            Command="{Binding SaveItemCommand}" />

                    <Button Grid.Row="0" Grid.Column="1" Margin="5"
                            Text="Cancel"
                            Command="{Binding CancelItemCommand}" />
                </Grid>
            </StackLayout>
        </StackLayout>

    </VerticalStackLayout>

</ContentPage>

内容视图:

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             xmlns:views="clr-namespace:MyApp.Pages.Views"
             x:Class="MyApp.Pages.Views.MyItemInputControlView"
             x:Name="this">

    <ContentView.Resources>
        <ResourceDictionary>
            <toolkit:InvertedBoolConverter x:Key="InvertBoolValueConverter" />
        </ResourceDictionary>
    </ContentView.Resources>

    <StackLayout BindingContext="{x:Reference this}">
        <Grid Margin="20, 0, 20, 0">
            <Grid.Resources>
                <!--<Style TargetType="Entry">
                    <Setter Property="Padding" Value="2 1" />
                    <Setter Property="BorderBrush" Value="LightGray" />
                </Style>-->
            </Grid.Resources>

            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>

            <StackLayout Grid.Row="0" Grid.Column="0" VerticalOptions="Center">
                <Label Text="{Binding NameLabelString}" />
                <Label Text="{Binding IsOptionalLabelString}"  FontSize="12" />
            </StackLayout>

            <StackLayout Grid.Row="0" Grid.Column="1" VerticalOptions="Center" >
                <Entry Text="{Binding EntryInput}" Placeholder="{Binding PlaceholderString}" Keyboard="{Binding KeyboardSetting}" Margin="5, 0, 5, 15" x:Name="entryControl">
                    <Entry.Behaviors>

                        <!--<toolkit:CharactersValidationBehavior IsValid="{Binding InputValid}"
                                                              Flags="ValidateOnUnfocusing,ValidateOnValueChanged"                                                         
                                                              CharacterType="Alphanumeric"
                                                              MinimumCharacterTypeCount="1" />-->

                    </Entry.Behaviors>
                </Entry>
                <Label Text="{Binding NameLabelString}" TextColor="Red" IsVisible="{Binding InputValid, Converter={StaticResource InvertBoolValueConverter}}"/>
            </StackLayout>

            <StackLayout Grid.Row="0" Grid.Column="2" VerticalOptions="Center" >
                <Label Text="{Binding MeasurementUnitString}"/>
            </StackLayout>
        </Grid>
    </StackLayout>

</ContentView>

简单保存按钮单击消息:用于将消息发送到不同的ViewModel

using CommunityToolkit.Mvvm.Messaging.Messages;

namespace MyApp.ViewModels.Messages
{
    public class SaveButtonClickedMessage : ValueChangedMessage<bool>
    {
        public SaveButtonClickedMessage(bool value) : base(value)
        {
            
        }
    }
}

内容视图代码隐藏:

namespace MyApp.Pages.Views;

public partial class MyItemInputControlView : ContentView
{
    public static readonly BindableProperty NameLabelProperty = BindableProperty.Create(nameof(NameLabelString), typeof(string), typeof(FoodItemInputControlView), string.Empty, BindingMode.OneWay);
    public static readonly BindableProperty IsOptionalProperty = BindableProperty.Create(nameof(IsOptionalLabelString), typeof(string), typeof(FoodItemInputControlView), string.Empty);
    public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create(nameof(PlaceholderString), typeof(string), typeof(FoodItemInputControlView), string.Empty);
    public static readonly BindableProperty MeasurementUnitProperty = BindableProperty.Create(nameof(MeasurementUnitString), typeof(string), typeof(FoodItemInputControlView), string.Empty);
    public static readonly BindableProperty KeyboardSettingProperty = BindableProperty.Create(nameof(KeyboardSetting), typeof(Keyboard), typeof(FoodItemInputControlView), Keyboard.Default);
    public static readonly BindableProperty EntryInputProperty = BindableProperty.Create(nameof(EntryInput), typeof(string), typeof(FoodItemInputControlView), string.Empty, BindingMode.TwoWay);

    public static readonly BindableProperty InputValidProperty = BindableProperty.Create(nameof(InputValid), typeof(bool), typeof(FoodItemInputControlView), true, BindingMode.OneWayToSource);

    public string NameLabelString
    {
        get => (string)GetValue(NameLabelProperty);
        set => SetValue(NameLabelProperty, value);
    }

    public string IsOptionalLabelString
    {
        get => (string)GetValue(IsOptionalProperty);
        set => SetValue(IsOptionalProperty, value);
    }

    public string PlaceholderString
    {
        get => (string)GetValue(PlaceholderProperty);
        set => SetValue(PlaceholderProperty, value);
    }

    public string MeasurementUnitString
    {
        get => (string)GetValue(MeasurementUnitProperty);
        set => SetValue(MeasurementUnitProperty, value);
    }

    public Keyboard KeyboardSetting
    {
        get => (Keyboard)GetValue(KeyboardSettingProperty);
        set => SetValue(KeyboardSettingProperty, value);
    }

    public string EntryInput
    {
        get => (string)GetValue(EntryInputProperty);
        set => SetValue(EntryInputProperty, value);
    }

    

    public bool InputValid
    {
        get => (bool)GetValue(InputValidProperty);
        set
        {
            // Before everything is initialized TextValidation should be valid
            if ((string.IsNullOrEmpty(IsOptionalLabelString) || IsOptionalLabelString.Equals("Optional")))
            {
                value = true;
            }

            SetValue(InputValidProperty, value);
        }
    }

    public MyItemInputControlView()
    {
        InitializeComponent();

        this.Loaded += MyItemInputControlView_Loaded;
        WeakReferenceMessenger.Default.Register<SaveButtonClickedMessage>(this, HandleSaveButtonClickedMessage);
    }

    private void HandleSaveButtonClickedMessage(object recipient, SaveButtonClickedMessage message)
    {
        var binding = new Binding("IsValid", BindingMode.TwoWay);
        charactersValidationBehavior.SetBinding(InputValidProperty, binding);

        charactersValidationBehavior.Flags = ValidationFlags.ValidateOnValueChanged | ValidationFlags.ValidateOnAttaching;
        charactersValidationBehavior.CharacterType = CharacterType.Alphanumeric;
        charactersValidationBehavior.MinimumCharacterTypeCount = 1;

        entryControl.Behaviors.Add(charactersValidationBehavior);
    }

    private void MyItemInputControlView_Loaded(object sender, EventArgs e)
    {
        entryControl.Behaviors.Remove(charactersValidationBehavior);
    }
}

内容页的视图模型:

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using MacroCalculatorApp.Models;
using SQLite;
using System.ComponentModel;

namespace MyApp.ViewModels
{
    public partial class ManageItemViewModel : ObservableObject, INotifyPropertyChanged
    {
        [ObservableProperty]
        private MyItem myItem;

        
        private bool InputsValid => true; // needs further evaluation if everything on the ContentView is correct


        [RelayCommand]
        async Task SaveItem()
        {
            WeakReferenceMessenger.Default.Send(new SaveButtonClickedMessage(true));

            try
            {
                if (InputsValid)
                {
                    ...

                    // Just navigate to the previous page
                    await Shell.Current.GoToAsync("..");
                }
            }
            catch (SQLiteException sqliteException)
            {
                ...
            }
            catch (Exception e)
            {
                await App.Current.MainPage.DisplayAlert("Error", e.Message, "OK");
            }
        }

        [RelayCommand]
        async Task CancelItem()
        {
            // Just navigate to the previous page
            await Shell.Current.GoToAsync("..");
        }

        public ManageMyItemViewModel()
        {
            MyItem = new MyItem();
        }
    }
}

我向ContentView的Code-Behind发送一个简单的Message(只包含值true)来检测按钮单击。
遗憾的是,当我分配CharactersValidationBehavior时,它不计算ContentViews的条目。此外,Bound**“InputValid”-属性的值未正确设置**,在调试时可能为false,然后标签可见并显示红色错误消息,当内容视图显示时。我可以用标签本身的“模式=双向”选项来抑制。因此,错误标签总是显示,也已经在我的内容视图加载时。
你知道我做错了什么吗?

相关问题