跳转至

IWindowService

IWindowService 提供了一种与平台无关的方式,用于从 ViewModel 中显示非模态窗口。ViewModel 与 Avalonia 窗口类型完全解耦。

主要功能包括:

  • 显示非模态窗口。
  • 由 ViewModel 驱动的窗口 — 所显示的窗口由自定义 ViewModel 定义。您可以使用 ViewModel 的 Content 属性,或使用应用于窗口 ViewModel 的 ViewLocatorAttribute 来指定目标窗口的内容。
  • 自动设置所有者 — 应用程序的活动窗口将成为目标窗口的所有者。活动窗口通过 IWindowsManager 服务自动解析。

接口定义

public interface IWindowService
{
    // Show a non-modal window driven by a ViewModel.
    void Show<T>(T viewModel, string? caption = null)
        where T : IWindowAwareViewModel;
}
参数 类型 说明
viewModel T 要显示的目标窗口的 ViewModel。viewModel 对象必须实现 IWindowAwareViewModel 接口。您可以从 WindowAwareViewModel 类派生窗口的 ViewModel,该类已实现此接口并增加了 View 定位基础结构。
caption string? 窗口标题。设置为 null 时,标题取自 ViewModel 的 Title 属性(IWindowAwareViewModel.Title)。

Show 方法在显示窗口后立即返回,不会等待窗口关闭。

异常:

  • ArgumentNullException — 如果 viewModel 参数为 null。

如何使用 IWindowService

  1. 为目标非模态窗口创建一个 ViewModel。例如,可以从 WindowAwareViewModel 类派生。
  2. 使用以下方式之一指定目标窗口的内容:

  3. 在需要显示目标窗口的 ViewModel 中,调用 IWindowService.Show 方法,并将目标窗口的 ViewModel 传递给 Show 方法。Show 方法会立即返回,所显示的窗口将保持打开,直到用户将其关闭。

访问服务

在 ViewModel 中访问 IWindowService 对象有两种方式:

  • 通过 Service<T>() 辅助方法。这是获取 Eremex Controls 库附带的已注册应用程序服务的便捷方法。
  • 通过构造函数注入。

Service() 辅助方法

在 ViewModel 中实现一个 Service<T>() 辅助方法,通过静态方法 ApplicationServicesContext.GetRequiredService<T>() 获取所请求的服务。

using Eremex.AvaloniaUI.Controls.ApplicationServices;
using CommunityToolkit.Mvvm.Input;

public partial class MyViewModel : ObservableObject
{
    // Provides access to any registered service
    protected static T Service<T>() where T : class
        => ApplicationServicesContext.GetRequiredService<T>();

    [RelayCommand]
    private void ShowWindow()
    {
        // Resolve the service and show the window
        Service<IWindowService>().Show(
            new MyWindowViewModel("Non-modal window"));
    }
}

在 App.axaml.cs 文件中,确保按如下方式使用 SimpleServiceProvider 和 ApplicationServicesContext 注册 Eremex 应用程序服务:

public class App : Application
{
    public override void OnFrameworkInitializationCompleted()
    {
        RegisterApplicationServices();
        //...
    }

    static void RegisterApplicationServices()
    {
        // Register built-in services.
        var serviceProvider = new SimpleServiceProvider();
        ApplicationServicesContext.RegisterApplicationServices(serviceProvider.AddSingleton);
        ApplicationServicesContext.SetCurrent(serviceProvider);
    }
}

构造函数注入

在 ViewModel 中实现一个以 IWindowService 为参数的构造函数。实例化 ViewModel 时,将服务对象传递给该构造函数。

public partial class MyViewModel
{
    private readonly IWindowService _windowService;

    public MyViewModel(IWindowService windowService)
    {
        _windowService = windowService;
    }

    [RelayCommand]
    private void ShowWindow()
    {
        _windowService.Show(new MyWindowViewModel("Non-modal window"));
    }
}

创建窗口的 ViewModel

您需要为要显示的非模态窗口创建一个 ViewModel。该 ViewModel 必须实现 IWindowAwareViewModel 接口。最简单的方法是从 WindowAwareViewModel 类派生 ViewModel,该类已实现 IWindowAwareViewModel 接口并负责 View 定位。

使用 ViewLocatorAttribute 定义窗口内容

  1. 创建一个 View(UserControl),其中包含要在目标窗口中显示的控件。
  2. 使用 ViewLocatorAttribute 修饰窗口的 ViewModel,并传入 View 的类型。

调用 IWindowService.Show 方法时,ViewModel 会创建一个 Avalonia 窗口,实例化指定的 View(UserControl),并将其作为窗口内容放入窗口中。ViewModel 永远无需直接引用 View 类型。

示例

以下示例使用 IWindowService 服务显示一个非模态窗口,其内容由自定义 View 指定。 完整代码请参阅 演示应用程序 应用程序。

app-services-iwindowservice-example

非模态窗口的 ViewModel

using Eremex.AvaloniaUI.Controls.ApplicationServices;

[ViewLocator(typeof(WindowServiceWindowView))]
public class WindowServiceWindowViewModel : WindowAwareViewModel
{
    private string statusText;

    public string StatusText
    {
        get => statusText;
        private set
        {
            if (statusText == value)
                return;

            statusText = value;
            OnPropertyChanged(nameof(StatusText));
        }
    }
}

非模态窗口的内容(View)

<UserControl x:Class="DemoCenter.Views.ApplicationServices.WindowServiceWindowView"
             xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="using:DemoCenter.ViewModels.ApplicationServices"
             x:CompileBindings="True"
             x:DataType="vm:WindowServiceWindowViewModel"
             MinWidth="420">

    <StackPanel Margin="20" Spacing="12">
        <TextBlock Text="Window content"
                   TextWrapping="Wrap" />

    </StackPanel>
</UserControl>

显示窗口

[RelayCommand]
private void ShowWindow()
{
    string windowTitle = "Non-modal window";
    Service<IWindowService>().Show(new WindowServiceWindowViewModel(windowTitle));
}

直接定义窗口内容

使用 ViewModel 的 Content 属性(WindowAwareViewModel.Content)显式提供窗口内容。

using Eremex.AvaloniaUI.Controls.ApplicationServices;

public class MyWindowViewModel : WindowAwareViewModel
{
    public MyWindowViewModel(string title) : base(title)
    {
        Content = "Window content";
    }
}

要显示此窗口,请使用以下代码:

Service<IWindowService>().Show(new MyWindowViewModel("Non-modal window"));

窗口标题

可以使用以下方式之一指定窗口标题:

  1. 设置 IWindowService.Show 方法的 caption 参数。
  2. 设置窗口 ViewModel 的 Title 属性。当 IWindowService.Show 方法的 caption 参数为 null 时,此属性生效。

所有者窗口

所有者通过 IWindowsManager 服务自动确定。该服务返回的应用程序活动窗口将成为目标窗口的所有者。



* 本页面使用机器翻译技术翻译。