.NET MAUI中ViewModel引用x:Name定义的TextValidationBehavior报错解决
在.NET MAUI中访问ContentView内TextValidationBehavior的IsValid属性
问题场景
我在.NET MAUI项目中创建了MyFoodInputControlView这个ContentView,其中Entry控件包含一个x:Name为myTextValidationBehavior的TextValidationBehavior。该ContentView被用于ManageItem页面,页面绑定的ManageItemsViewModel中,SaveItem命令需要访问这个TextValidationBehavior的IsValid属性,但直接引用时提示“名称'myTextValidationBehavior'在当前上下文中不存在”,请问如何正确获取该验证状态?
相关代码
ContentView代码(MyFoodInputControlView.xaml)
<?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" x:Class="MyApp.Pages.Views.MyFoodInputControlView" x:Name="this"> <StackLayout BindingContext="{x:Reference this}"> <Grid Margin="20, 0, 20, 0"> <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"> <Entry.Behaviors> <toolkit:TextValidationBehavior Flags="ValidateOnValueChanged" x:Name="myTextValidationBehavior" toolkit:MultiValidationBehavior.Error="Entry may not be empty"> </toolkit:TextValidationBehavior> </Entry.Behaviors> </Entry> </StackLayout> </Grid> </StackLayout> </ContentView>
ContentPage代码(ManageItem.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" xmlns:viewModels="clr-namespace:MyApp.ViewModels" xmlns:controls="clr-namespace:MyApp.Pages.Views" x:DataType="viewModels:ManageItemsViewModel" x:Class="MyApp.Pages.ManageItem" Title="My Title"> <VerticalStackLayout> <StackLayout> <controls:MyFoodInputControlView NameLabelString="MyLabel1:" IsOptionalLabelString="Mandatory" PlaceholderString="e.g. This Placeholder" EntryInput="{Binding FoodNameString}" /> <controls:MyFoodInputControlView NameLabelString="MyLable2:" IsOptionalLabelString="Optional" PlaceholderString="z.B. This Placeholder" EntryInput="{Binding BrandNameString}" /> <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>
ViewModel代码(ManageItemsViewModel.cs)
namespace MyApp.ViewModels { public partial class ManageItemsViewModel : ObservableObject { [ObservableProperty] private string foodNameString; [ObservableProperty] private string brandNameString; [RelayCommand] async Task SaveItem() { try { if (!myTextValidationBehavior.IsValid) { // 验证失败逻辑 } // 保存逻辑 } catch (Exception e) { await App.Current.MainPage.DisplayAlert("Error", e.Message, "OK"); } } [RelayCommand] async Task CancelItem() { // 取消逻辑 } } }
解决方案
直接在ViewModel中引用ContentView内部的myTextValidationBehavior违反了MVVM的分层原则,正确的做法是通过绑定属性将验证状态暴露给外部,再让ViewModel获取该状态。
步骤1:给ContentView添加绑定属性暴露验证状态
在MyFoodInputControlView.xaml.cs中,添加一个绑定属性IsInputValid,并监听内部TextValidationBehavior的IsValid变化来同步值:
using Microsoft.Maui.Controls; using CommunityToolkit.Maui.Behaviors; namespace MyApp.Pages.Views; public partial class MyFoodInputControlView : ContentView { // 暴露输入验证状态的绑定属性 public static readonly BindableProperty IsInputValidProperty = BindableProperty.Create(nameof(IsInputValid), typeof(bool), typeof(MyFoodInputControlView), true); public bool IsInputValid { get => (bool)GetValue(IsInputValidProperty); set => SetValue(IsInputValidProperty, value); } // 其他现有绑定属性(NameLabelString、EntryInput等)保持不变 public static readonly BindableProperty NameLabelStringProperty = BindableProperty.Create(nameof(NameLabelString), typeof(string), typeof(MyFoodInputControlView)); public string NameLabelString { get => (string)GetValue(NameLabelStringProperty); set => SetValue(NameLabelStringProperty, value); } public static readonly BindableProperty EntryInputProperty = BindableProperty.Create(nameof(EntryInput), typeof(string), typeof(MyFoodInputControlView)); public string EntryInput { get => (string)GetValue(EntryInputProperty); set => SetValue(EntryInputProperty, value); } // 省略其他属性的定义... public MyFoodInputControlView() { InitializeComponent(); // 监听验证行为的状态变化,同步到绑定属性 myTextValidationBehavior.PropertyChanged += (sender, e) => { if (e.PropertyName == nameof(TextValidationBehavior.IsValid)) { IsInputValid = myTextValidationBehavior.IsValid; } }; } }
步骤2:在ViewModel中绑定验证状态
在ManageItemsViewModel中添加对应属性,用来接收控件的验证状态:
namespace MyApp.ViewModels { public partial class ManageItemsViewModel : ObservableObject { [ObservableProperty] private string foodNameString; [ObservableProperty] private string brandNameString; // 新增:接收食物名称输入的验证状态 [ObservableProperty] private bool isFoodNameValid; [RelayCommand] async Task SaveItem() { try { // 现在可以直接判断验证状态 if (!IsFoodNameValid) { await App.Current.MainPage.DisplayAlert("提示", "食物名称不能为空", "确定"); return; } // 执行保存逻辑 } catch (Exception e) { await App.Current.MainPage.DisplayAlert("Error", e.Message, "OK"); } } [RelayCommand] async Task CancelItem() { // 取消逻辑 } } }
步骤3:在ContentPage中关联绑定
修改ManageItem.xaml,给必填的MyFoodInputControlView添加IsInputValid的绑定,设置Mode=OneWayToSource让控件状态同步到ViewModel:
<controls:MyFoodInputControlView NameLabelString="MyLabel1:" IsOptionalLabelString="Mandatory" PlaceholderString="e.g. This Placeholder" EntryInput="{Binding FoodNameString}" IsInputValid="{Binding IsFoodNameValid, Mode=OneWayToSource}" />
补充说明
- 如果需要验证多个输入控件,只需给每个控件添加对应的绑定属性和ViewModel属性,然后在
SaveItem中逐一判断即可。 - 这种方式符合MVVM的分层思想,ViewModel不需要直接依赖视图元素,仅通过绑定获取状态,代码更易维护和测试。
内容的提问来源于stack exchange,提问作者OXO
相关产品推荐
相关产品推荐

