2/7

2/7

NET Core3環境でMVVMフレームワークPrismを使用する方法

最后更新 2023/06/10 23:41
RyzenAdorer
预计阅读 10 分钟
分类
Blazor
专题
WPF MVVMフレームワークPrismシリーズ
标签
.NET C# Blazor WPF Prism

この記事は転載から

著者:Ryzen Adorer

前の記事:. NET Core 3 WPF MVVMフレームワークPrismシリーズ

原文へのリンク:https//www.example.com

この記事では、. NET Core 3環境でMVVMフレームワークPrismコマンドを使用する方法を説明します。

I.はじめに作成DelegateCommandコマンド

我们在上一篇.NET Core 3 WPF MVVM 框架 Prism 系列之数据绑定中知道 prism 实现数据绑定的方式,我们按照标准的写法来实现,我们分别创建 Views 文件夹和 ViewModels 文件夹,将 MainWindow 放在 Views 文件夹下,再在 ViewModels 文件夹下面创建 MainWindowViewModel 类,如下:

Xamlコードは以下の通り。

<Window
  x:Class="CommandSample.Views.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:prism="http://prismlibrary.com/"
  xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:CommandSample"
  mc:Ignorable="d"
  Title="MainWindow"
  Height="350"
  Width="450"
  prism:ViewModelLocator.AutoWireViewModel="True"
>
  <StackPanel>
    <TextBox Margin="10" Text="{Binding CurrentTime}" FontSize="32" />
    <button
      x:Name="mybtn"
      FontSize="30"
      Content="Click Me"
      Margin="10"
      Height="60"
      Command="{Binding GetCurrentTimeCommand}"
    />
    <Viewbox Height="80">
      <CheckBox
        IsChecked="{Binding IsCanExcute}"
        Content="CanExcute"
        Margin="10"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
      />
    </Viewbox>
  </StackPanel>
</Window>

MainWindowViewModelクラス·コードは次のとおりです。

using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Windows.Controls;

namespace CommandSample.ViewModels
{
   public class MainWindowViewModel: BindableBase
    {
        private bool _isCanExcute;
        public bool IsCanExcute
        {
            get { return _isCanExcute; }
            set
            {
                SetProperty(ref _isCanExcute, value);
                GetCurrentTimeCommand.RaiseCanExecuteChanged();
            }
        }

        private string _currentTime;
        public string CurrentTime
        {
            get { return _currentTime; }
            set { SetProperty(ref _currentTime, value); }
        }

        private DelegateCommand _getCurrentTimeCommand;
        public DelegateCommand GetCurrentTimeCommand =>
            _getCurrentTimeCommand ?? (_getCurrentTimeCommand = new DelegateCommand(ExecuteGetCurrentTimeCommand, CanExecuteGetCurrentTimeCommand));

        void ExecuteGetCurrentTimeCommand()
        {
            this.CurrentTime = DateTime.Now.ToString();
        }

        bool CanExecuteGetCurrentTimeCommand()
        {
            return IsCanExcute;
        }
    }
}

動作効果は以下の通り:

コードでは、Prism. Mvvmを使用してBindableBaseの継承を導入しています。これは、前回の記事で知ったプロパティ変更通知メソッドSet Propertyを使用する必要があるためです。Prism. Commandsを使用すると、定義したDelegateCommand型がこの名前空間の下にあり、ICommandインターフェイスには3つの関数メンバ、イベントCanExecuteChanged、戻り値bool、objectを引数とするCanExecuteメソッドがあることがわかります。戻り値を持たず、オブジェクトに引数を1つ取るExecuteメソッドです。GetCurrentTimeCommandコマンドを実装していることは明らかです。

もう一つ注目すべき点は、CheckboxのIsChecked経由でboolプロパティIsCanExcuteをバインドし、CanExecuteメソッドでIsCanExcuteを返します。CanExecuteはExecuteメソッドの実行可能性とButtonのIsEnable状態を制御することがわかっていますが、IsCanExcuteのsetメソッドでは次のような文を追加しました。

GetCurrentTimeCommand.RaiseCanExecuteChanged();

実際、PrismソースコードからRaiseCanExecuteChangedメソッドは、ICommandインターフェイス内のCanExecuteChangedイベントを呼び出してCanExecuteメソッドを呼び出す内部呼び出しであることがわかります。

public void RaiseCanExecuteChanged()
{
    OnCanExecuteChanged();
}

protected virtual void OnCanExecuteChanged()
{
    EventHandler handler = this.CanExecuteChanged;
    if (handler != null)
    {
        if (_synchronizationContext != null && _synchronizationContext != SynchronizationContext.Current)
        {
            _synchronizationContext.Post(delegate
            {
                handler(this, EventArgs.Empty);
            }, null);
        }
        else
        {
            handler(this, EventArgs.Empty);
        }
    }
}

実際、上記のプリズムはより簡潔でエレガントな記述も提供しています:

private bool _isCanExcute;
 public bool IsCanExcute
 {
    get { return _isCanExcute; }
    set { SetProperty(ref _isCanExcute, value);}
 }

 private DelegateCommand _getCurrentTimeCommand;
 public DelegateCommand GetCurrentTimeCommand =>
    _getCurrentTimeCommand ?? (_getCurrentTimeCommand = new  DelegateCommand(ExecuteGetCurrentTimeCommand).ObservesCanExecute(()=> IsCanExcute));

 void ExecuteGetCurrentTimeCommand()
 {
    this.CurrentTime = DateTime.Now.ToString();
 }

ObservesCanExecuteメソッドが使用されますが、このメソッド内でRaiseCanExecuteChangedメソッドも呼び出します。

上記のコードでは、2つの質問をすることができます:

  • パラメータ付きのDelegateCommandを作成するには?
  • コントロールに依存属性Commandが含まれていない場合、コントロールのイベントを使用したい場合、コマンドに変換するには?

II.はじめにDelegateCommandパラメータ付きコマンドの作成

パラメータ付きコマンドを作成する前に、DelegateCommandの継承チェーンと公開されたパブリックメソッドを見てみましょう。詳細な実装はソースコードを参照してください。

那么,其实已经很明显了,我们之前创建 DelegateCommand 不是泛型版本,当创建一个泛型版本的DelegateCommand<T>,那么 T 就是我们要传入的命令参数的类型,那么,我们现在可以把触发命令的 Button 本身作为命令参数传入

Xamlコードは以下の通り。

<button
  x:Name="mybtn"
  FontSize="30"
  Content="Click Me"
  Margin="10"
  Height="60"
  Command="{Binding GetCurrentTimeCommand}"
  CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}}"
/>

GetCurrentTimeCommandコマンドのコードを次のように変更します。

private DelegateCommand<object> _getCurrentTimeCommand;
public DelegateCommand<object> GetCurrentTimeCommand =>
    _getCurrentTimeCommand ?? (_getCurrentTimeCommand = new DelegateCommand<object>(ExecuteGetCurrentTimeCommand).ObservesCanExecute(()=> IsCanExcute));

 void ExecuteGetCurrentTimeCommand(object parameter)
 {
    this.CurrentTime =((Button)parameter)?.Name+ DateTime.Now.ToString();
 }

実装効果を見てみましょう:

III.ザ·フーイベントの回転コマンド

Command依存プロパティを持つコントロールのほとんどは、ICommandSourceインターフェイスを継承しているためです。ICommandSourceインターフェイスは3つの関数メンバー ICommandInterface型プロパティCommand、object型プロパティCommandParameter、IInputElement型プロパティCommandTargetを持ち、ICommandSourceインターフェイスの基本クラスはButtonBaseとMenuItemなので、ButtonBase、Checkbox、RadioButtonなどはButtonBaseからCommand依存プロパティを継承しています。Menuitemも同様です。通常のテキストボックスにはありません。

今、我々はこのニーズを持っている、我々はこのインターフェイスに基づいて2番目のテキストボックスを追加したい、テキストボックスのテキストが変更されるとき、ボタンの名前と2番目のテキストボックスのテキスト文字列をマージし、最初のテキストボックスに更新する必要があります、我々の最初の直感は確かにテキストボックスのTextChangedイベントを使用することを考えます。

まず、Xamlインターフェイスを紹介します:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

アセンブリSystem.Windows.Interactivity dllはExpression Blend SDKに含まれており、Prismパッケージにも含まれているので、直接インポートして2番目のTextboxコードを追加します。

<TextBox
  Margin="10"
  FontSize="32"
  Text="{Binding Foo,UpdateSourceTrigger=PropertyChanged}"
>
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="TextChanged">
      <i:InvokeCommandAction
        Command="{Binding TextChangedCommand}"
        CommandParameter="{Binding ElementName=mybtn}"
      />
    </i:EventTrigger>
  </i:Interaction.Triggers>
</TextBox>

MainWindowViewModelの新しいコード:

private string _foo;
public string Foo
{
     get { return _foo; }
     set { SetProperty(ref _foo, value); }
}

private DelegateCommand<object> _textChangedCommand;
public DelegateCommand<object> TextChangedCommand =>
  _textChangedCommand ?? (_textChangedCommand = new DelegateCommand<object>(ExecuteTextChangedCommand));

void ExecuteTextChangedCommand(object parameter)
{
  this.CurrentTime = Foo + ((Button)parameter)?.Name;
}

実施効果は以下の通り。

上記のxamlコードでは、TextBoxのTextChangedイベントのBlend EventTriggerをリッスンし、イベントがトリガーされるたびにInvokeCommandActionがTextChangedCommandコマンドを呼び出します。

3.1.コマンド#にEventArgsパラメータを渡す

TextChangedイベントにはRoutedEventArgsパラメータTextChangedEventArgsがあることはわかっていますが、TextChangedEventArgsまたはRoutedEventArgsパラメータ内のプロパティを取得したい場合、取得方法は、System.Windows.InteractivityのNameSpaceでInvokeCommandActionを使用することではできません。この場合、prismのInvokeCommandActionのTriggerParameterPathプロパティを使用する必要があります。2番目のText Box入力文字列とイベントをトリガーしたコントロールの名前を表示すると、親クラスRoutedEventArgsのSoucreプロパティを使用でき、イベントをトリガーしたコントロールが2番目のText Boxです。

Xamlコードを以下のように修正。

<TextBox
  x:Name="myTextBox"
  Margin="10"
  FontSize="32"
  Text="{Binding Foo,UpdateSourceTrigger=PropertyChanged}"
  TextChanged="TextBox_TextChanged"
>
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="TextChanged">
      <prism:InvokeCommandAction
        Command="{Binding TextChangedCommand}"
        TriggerParameterPath="Source"
      />
    </i:EventTrigger>
  </i:Interaction.Triggers>
</TextBox>

MainWindowViewModelを以下のように変更する。

void ExecuteTextChangedCommand(object parameter)
{
    this.CurrentTime = Foo + ((TextBox)parameter)?.Name;
}

効果の実現:

もう一つ興味深いことに、上記のxamlコードがTriggerParameterPathを削除すると、TextChangedEventArgsが得られます。

IV.ザ·フータスクベースのコマンドの実装

まず、インターフェイスに新しいタスクベースのコマンドをバインドする新しいボタンを追加します。ボタンをクリックすると、最初のTextboxには5秒後に“Hello Prism!”が表示されます。“”、期間UIインタフェースがブロックされない場合

Xamlインターフェイスの新しいボタンコードは以下の通りです。

<button
  x:Name="mybtn1"
  FontSize="30"
  Content="Click Me 1"
  Margin="10"
  Height="60"
  Command="{Binding AsyncCommand}"
/>

MainWindowViewModelの新しいコード:

private DelegateCommand _asyncCommand;
  public DelegateCommand AsyncCommand =>
     _asyncCommand ?? (_asyncCommand = new DelegateCommand(ExecuteAsyncCommand));

  async void ExecuteAsyncCommand()
  {
     await ExampleMethodAsync();
  }

  async Task ExampleMethodAsync()
  {
     await Task.Run(()=>
     {
        Thread.Sleep(5000);
        this.CurrentTime = "Hello Prism!";
     } );
  }

もっと簡潔に書くこともできます:

private DelegateCommand _asyncCommand;
 public DelegateCommand AsyncCommand =>
    _asyncCommand ?? (_asyncCommand = new DelegateCommand( async()=>await ExecuteAsyncCommand()));

 Task ExecuteAsyncCommand()
 {
    return Task.Run(() =>
    {
       Thread.Sleep(5000);
       this.CurrentTime = "Hello Prism!";
    });
  }

直接効果を見る:

V.ザ·フー複合の作成コマンド

prismは複合コマンドをサポートするCompositeCommandクラスを提供しています複合コマンドとは何ですか?このシナリオがあるかもしれませんメインインターフェイスの異なるサブフォームにはそれぞれのビジネスがあります上記の例を少し変更することができます3つの異なるサブフォームに分割します3つはそれぞれ現在の年、月、時間、秒を表示しますメインフォームにボタンを提供したいですクリックすると同時に表示できるようにしますこの時点で関係がありますメインフォームのボタンは3つのサブフォームのボタンに依存し、サブフォームのボタンはメインフォームのボタンに依存しません

以下は、prism標準複合コマンドの作成と使用の手順です。

  • グローバルな複合コマンドの作成
  • IOCコンテナによるシングルトンとして登録
  • 複合コマンドのサブコマンドの登録
  • 複合結合コマンドのバインド

5.1.グローバルな複合コマンドの作成

まず、グローバル·コマンド·クラスとしてApplicationCommandsクラスを追加するクラスライブラリ·プロジェクトを次のコードで作成します。

public interface IApplicationCommands
{
    CompositeCommand GetCurrentAllTimeCommand { get; }
}

public class ApplicationCommands : IApplicationCommands
{
   private CompositeCommand _getCurrentAllTimeCommand = new CompositeCommand();
   public CompositeCommand GetCurrentAllTimeCommand
   {
        get { return _getCurrentAllTimeCommand; }
   }
}

IApplicationCommandsインターフェイスを作成し、次にIOCコンテナにグローバルシングルトンインターフェイスとして登録するためにApplicationCommandsに実装させました。

5.2. IOCコンテナによるシングルトンとして登録

メインフォームとして新しいプロジェクトを作成し、サブフォームを表示し、複合コマンドを使用します。キーセクションは次のとおりです。

app.csコード:

using Prism.Unity;
using Prism.Ioc;
using System.Windows;
using CompositeCommandsSample.Views;
using Prism.Modularity;
using CompositeCommandsCore;

namespace CompositeCommandsSample
{

 public partial class App : PrismApplication
 {
     protected override Window CreateShell()
     {
         return Container.Resolve<MainWindow>();
     }

     //通过IOC容器注册IApplicationCommands为单例
     protected override void RegisterTypes(IContainerRegistry containerRegistry)
     {
        containerRegistry.RegisterSingleton<IApplicationCommands, ApplicationCommands>();
     }

     //注册子窗体模块
     protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
     {
        moduleCatalog.AddModule<CommandSample.CommandSampleMoudle>();
     }
  }
}

5.3.複合コマンドのサブコマンドの登録

以前のCommandSampleソリューションの下にあるViewsフォルダに2つのUser Controlを追加し、それぞれ月と時を表示します。ViewModelsフォルダに2つのUser Control ViewModelを追加し、以前のMainWindowをUser Controlに変更しました。

主要部分のコード:

GetHourTabViewModel.cs:

IApplicationCommands _applicationCommands;

public GetHourTabViewModel(IApplicationCommands applicationCommands)
{
    _applicationCommands = applicationCommands;
    //给复合命令GetCurrentAllTimeCommand注册子命令GetHourCommand
    _applicationCommands.GetCurrentAllTimeCommand.RegisterCommand(GetHourCommand);
}

private DelegateCommand _getHourCommand;
public DelegateCommand GetHourCommand =>
   _getHourCommand ?? (_getHourCommand = new DelegateCommand(ExecuteGetHourCommand).ObservesCanExecute(() => IsCanExcute));

void ExecuteGetHourCommand()
{
   this.CurrentHour = DateTime.Now.ToString("HH:mm:ss");
}

GetMonthDayTabViewModel.cs:

IApplicationCommands _applicationCommands;

 public GetMonthDayTabViewModel(IApplicationCommands applicationCommands)
 {
     _applicationCommands = applicationCommands;
     //给复合命令GetCurrentAllTimeCommand注册子命令GetMonthCommand
     _applicationCommands.GetCurrentAllTimeCommand.RegisterCommand(GetMonthCommand);
 }

 private DelegateCommand _getMonthCommand;
 public DelegateCommand GetMonthCommand =>
      _getMonthCommand ?? (_getMonthCommand = new DelegateCommand(ExecuteCommandName).ObservesCanExecute(()=>IsCanExcute));

 void ExecuteCommandName()
 {
    this.CurrentMonthDay = DateTime.Now.ToString("MM:dd");
 }

MainWindowViewModel.cs:

IApplicationCommands _applicationCommands;

public MainWindowViewModel(IApplicationCommands applicationCommands)
{
    _applicationCommands = applicationCommands;
    //给复合命令GetCurrentAllTimeCommand注册子命令GetYearCommand
    _applicationCommands.GetCurrentAllTimeCommand.RegisterCommand(GetYearCommand);
}

private DelegateCommand _getYearCommand;
public DelegateCommand GetYearCommand =>
   _getYearCommand ?? (_getYearCommand = new DelegateCommand(ExecuteGetYearCommand).ObservesCanExecute(()=> IsCanExcute));

void ExecuteGetYearCommand()
{
   this.CurrentTime =DateTime.Now.ToString("yyyy");
}

CommandSampleMoudle.cs:

using CommandSample.ViewModels;
using CommandSample.Views;
using Prism.Ioc;
using Prism.Modularity;
using Prism.Regions;

namespace CommandSample
{
  public class CommandSampleMoudle : IModule
  {
    public void OnInitialized(IContainerProvider containerProvider)
    {
       var regionManager = containerProvider.Resolve<IRegionManager>();
       IRegion region= regionManager.Regions["ContentRegion"];

       var mainWindow = containerProvider.Resolve<MainWindow>();
       (mainWindow.DataContext as MainWindowViewModel).Title = "GetYearTab";
       region.Add(mainWindow);

       var getMonthTab = containerProvider.Resolve<GetMonthDayTab>();
       (getMonthTab.DataContext as GetMonthDayTabViewModel).Title = "GetMonthDayTab";
       region.Add(getMonthTab);

       var getHourTab = containerProvider.Resolve<GetHourTab>();
       (getHourTab.DataContext as GetHourTabViewModel).Title = "GetHourTab";
       region.Add(getHourTab);
    }

    public void RegisterTypes(IContainerRegistry containerRegistry)
    {

    }
  }
}

5.4.複合結合コマンドのバインド

メインフォームXamlコード:

<Window
  x:Class="CompositeCommandsSample.Views.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:prism="http://prismlibrary.com/"
  xmlns:local="clr-namespace:CompositeCommandsSample"
  mc:Ignorable="d"
  prism:ViewModelLocator.AutoWireViewModel="True"
  Title="MainWindow"
  Height="650"
  Width="800"
>
  <Window.Resources>
    <style TargetType="TabItem">
      <Setter Property="Header" Value="{Binding DataContext.Title}"/>
    </style>
  </Window.Resources>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="auto" />
      <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <button
      Content="GetCurrentTime"
      FontSize="30"
      Margin="10"
      Command="{Binding ApplicationCommands.GetCurrentAllTimeCommand}"
    />
    <TabControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />
  </Grid>
</Window>

MainWindowViewModel.cs:

using CompositeCommandsCore;
using Prism.Mvvm;

namespace CompositeCommandsSample.ViewModels
{
  public  class MainWindowViewModel:BindableBase
  {
    private IApplicationCommands _applicationCommands;
    public IApplicationCommands  ApplicationCommands
    {
       get { return _applicationCommands; }
       set { SetProperty(ref _applicationCommands, value); }
    }

    public MainWindowViewModel(IApplicationCommands applicationCommands)
    {
        this.ApplicationCommands = applicationCommands;
    }
  }
}

実際の効果を見てみましょう:

最後に、複合コマンドも最初に述べた関係を検証します。複合コマンドはサブコマンドに依存しますが、サブコマンドは複合コマンドに依存しません。したがって、複合コマンドは3つのサブコマンドすべてが実行可能な場合にのみ実行できます。プリsmモジュール性の知識を使用して、次の記事で詳しく説明します。

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2024/11/06

なぜ私のブログはBlazorに戻るのか?

ブログサイトの開発は苦労し、MVC、Vue、Goなどの10近くのバージョンを試してきましたが、Blazorに戻り、静的SSRを使用して、速度が急上昇し、正常にオンラインになりました。

继续阅读
同分类 / 同标签 2024/02/29

Winformでもデータを表示できます。

winform開発の過程では、しばしばデータ表示機能を行う必要がありますが、以前はグリッドコントロールを使用していましたが、今日は例を通じて、Winform Blazorハイブリッドでant Design Blazorテーブルコンポーネントを使用してデータ表示を行う方法を紹介します。

继续阅读