我在 .NET MAUI MVVM 项目中有一个带有输入控件和两个按钮的视图。当视图加载时,输入控件未获得焦点。如何将焦点设置到入口控件而不在后面的代码中添加功能? IE。 ContentPage 后面的代码只是将 ViewModel 链接到 BindingContext。
据我所知,表单控件不应该直接从 ViewModel 访问,那么在表单加载时是否可以通过其他方式将焦点设置到条目控件?
我可以让它工作的唯一方法是放入该 xaml 来实现我想要的焦点控制:
Loaded="OnEntryLoaded"
然后在后面的代码中:
private void OnEntryLoaded(object sender, EventArgs e)
{
Name.Focus();
}
正如您在问题中提到的,如果没有 .cs 中的任何代码,我们就无法在 ViewModel 中设置 Entry 焦点。
最简单的方法是在 OnAppearing 方法中设置焦点(允许用户在页面可见之前立即自定义行为):
protected override void OnAppearing()
{
base.OnAppearing();
Task.Run(() =>
{
while (!myentry.IsVisible)
{
Task.Delay(10).Wait();
}
Application.Current.Dispatcher.Dispatch(() =>
{
myentry.Focus();
});
});
}
希望它对你有用。
简介/背景:
我尝试将对视图的引用限制为仅在视图(xaml)本身内。理想情况下,这意味着 xaml 中没有任何 x:Name="Whatever" ,除非同一 xaml 中的视图引用 xaml 中的另一个视图,如下所示。这样做的一个好处是,例如,我可以将具有 20 个子视图的父视图减少为单个子视图,以便进行故障排除并将我的注意力集中在该单个视图上,然后任何属性更改都是由于该视图而不是其他 19 个视图中的任何一个。此外,如果我需要使视图在 Windows 和 iOS 中具有不同的行为,那么如果我决定进行较大的更改,那么没有这些引用会让事情变得更容易。
解决方案:
您提出的问题无需任何后端代码即可解决。以下是设置步骤的概述:
我假设步骤 1 和 2 很简单。 (我一直在使用“CommunityToolkit.Maui”版本 7.0.0,在我的 .NET 8 项目中没有任何问题,但是当我尝试添加最新版本时,Visual Studio 抱怨 Android 上下文,所以这就是我回到版本 7 的原因工具包的内容。这对于本次讨论来说是微不足道的。)
第 3 步之后,MauiProgram.cs 应该是这样的(主要兴趣点是“.UseMauiCommunityToolkit()”及其关联的 using 指令):
using CommunityToolkit.Maui;
using Microsoft.Extensions.Logging;
namespace FocusExperiment
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
// Initialize the .NET MAUI Community Toolkit by adding the below line of code
.UseMauiCommunityToolkit()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
}
关于第4步,我选择删除MainPage并仅添加一个新的XAML文件,以强调不涉及View的后端代码。我只需向项目添加一个新的 XAML ContentPage 并接受其默认名称“NewPage1.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:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:l="clr-namespace:FocusExperiment"
x:Class="FocusExperiment.NewPage1"
Title="NewPage1">
<ContentPage.BindingContext>
<l:MyViewModel />
</ContentPage.BindingContext>
<ContentPage.Behaviors>
<toolkit:EventToCommandBehavior
EventName="Loaded"
Command="{Binding LoadedCommand}"
CommandParameter="{x:Reference EntryControl}"
/>
</ContentPage.Behaviors>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button
Grid.Row="0"
Text="Hello"
/>
<Button
Grid.Row="1"
Text="World"
/>
<Entry
Grid.Row="2"
/>
<!--This one gets the focus:-->
<Entry
Grid.Row="3"
x:Name="EntryControl"
/>
<Entry
Grid.Row="4"
/>
</Grid>
</ContentPage>
我在那里放置了 3 个入口视图,并将焦点放在中间的一个上。这样,如果有任何默认行为将焦点放在第一个或最后一个输入控件上,我就不会被它愚弄。
记下“工具包:EventToCommandBehavior”节点。它将行为附加到 ContentPage。 EventName 告诉它监听 ContentPage 的 Loaded 事件。当该事件被广播时,附加的行为将调用 ICommand::Execute 方法(假设 ICommand::CanExecute 返回 true)。我正在传递我想要接收焦点的视图;我使用 CommandParameter 并给它“{x:Reference EntryControl}”来做到这一点。这是可以使用“x:Name=...”的地方,因为它既不耦合到后端也不耦合到视图模型。
另请注意,视图模型是通过“
这是第 5 步的视图模型:
using System.Windows.Input;
namespace FocusExperiment;
public class MyViewModel : BindableObject
{
private class Loaded : ICommand
{
public event EventHandler? CanExecuteChanged;
private bool _canExecute = true;
public bool CanExecute(object? parameter)
{
if (parameter is bool canExecute && _canExecute != canExecute)
{
_canExecute = canExecute;
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
return _canExecute;
}
public void Execute(object? parameter)
{
if (parameter is View view)
{
view.Focus();
}
}
}
public ICommand LoadedCommand
{
get { return (ICommand)GetValue(LoadedCommandProperty); }
set { SetValue(LoadedCommandProperty, value); }
}
public static readonly BindableProperty LoadedCommandProperty = BindableProperty.Create(nameof(LoadedCommand)
, typeof(ICommand), typeof(MyViewModel), new Loaded());
}
注意:通过CommandParameter传递的View,其Focus方法是在ICommand::Execute方法中调用的。
最后(第 6 步),将 AppShell.xaml 修改为如下所示:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="FocusExperiment.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FocusExperiment"
Shell.FlyoutBehavior="Disabled"
Title="FocusExperiment">
<ShellContent
Title="Home"
ContentTemplate="{DataTemplate local:NewPage1}"
Route="NewPage1"
/>
</Shell>