VB のたまご

作成日: 2017/05/14, 更新日: 2017/05/14



IActiveAwareCommand

  •  ここでは、IActiveAwareCommand について学習します。 誤解を招くような書き方をした私が悪いのですが、【IActiveAwareCommand】というクラスのコマンドがあるわけではなく、 CompositeCommand が持つもう一個の機能のことです。すみません。

  • スポンサーリンク


  •  前回学習した CompositeCommand は、登録したコマンド全てを対象に動作していました。 しかし、別モードでインスタンス生成した CompositeCommand は、登録したコマンドのうち、 アクティブな(動作可能な)コマンドのみを対象に動作するようになります。

  •  アクティブなコマンドがどういう状態かと言うと、コマンドが表示されている View がアクティブ(表示中)になっている状態を言います。 想定するシナリオとしては、TabControl の1ページ分や ListBox の1項目分など、複数項目の中の1項目を選択中は、 そのアクティブな View にあるコマンドのみを実行したい場合に使います。 こんなイメージです。

  • イメージ
  •  それでは、具体的に使い方を見てみましょう。

  •  まずは、別モードで CompositeCommand をインスタンス生成します。別モードは、引数に True をセットします。 この True というのは、「アクティブなコマンドのみ監視するかどうか」の True です。 インスタンス生成時、引数を省略するか False を指定すると、全てのコマンドを監視するようになります。

  • Private _ActiveMonitorCommand As CompositeCommand
    _ActiveMonitorCommand = New CompositeCommand(True)
    

  •  次に複数選択の1項目分となる画面を作成します。ここでは TabControl にインジェクションする画面ですね。 そして、ここが大事なポイントですが、この画面にバインドする ViewModel に IActiveAware インターフェースを実装します。 このインターフェースが、今自分がアクティブなのかどうかを知らせてくれます。

  • Public Class TabViewModel
        Inherits BindableBase
        Implements IActiveAware
    
        ~省略~
    
        Private _IsActive As Boolean
        Public Property IsActive As Boolean Implements IActiveAware.IsActive
            Get
                Return Me._IsActive
            End Get
            Set(value As Boolean)
    
                Me._IsActive = value
                Me.UpdateCommand.IsActive = value
                RaiseEvent IsActiveChanged(Me, New EventArgs)
    
            End Set
        End Property
    
        Public Event IsActiveChanged As EventHandler Implements IActiveAware.IsActiveChanged
    
    End Class
    

  •  アクティブなタブページが切り替わった瞬間に、IsActive プロパティが切り替わります。 このタイミングで一緒にコマンドの状態も切り替えます。 DelegateCommand 側にも IsActive プロパティがありますので、これを使います。

  •  CompositeCommand コマンドは、シングルトン形式で扱います。 Unity コンテナ経由でインジェクションしてもらい、画面毎に定義した DelegateCommand コマンドを CompositeCommand コマンドに登録していきます。

  •  このサンプルを実行すると、2つのメインボタンとタブページ3つが表示されます。 上部にある2つのボタンはそれぞれ CompositeCommand コマンドをバインドしており、各ページには、チェック欄とボタンが表示されています。

  •  「全ボタンを対象に実行」ボタンが通常の CompositeCommand、「表示中のボタンを対象に実行」ボタンがアクティブなボタンのみを追跡する CompositeCommand です。 実際に動かして、違いを見てみてください。

  • スポンサーリンク


  •  それでは、以下ソース全体です。

  •  今回のプロジェクト構成は3つに分かれており、CompositeCommand を管理・提供するプロジェクト、 タブページ1つ分の画面を管理するプロジェクト、メイン画面を管理するプロジェクトになっています。

  •  まずは、CompositeCommand を管理・提供するプロジェクトです。クラスライブラリで作成しています。

  •  ICommonFolder.vb

  • Imports Prism.Commands
    
    Public Interface ICommonFolder
    
        ' 登録した全コマンドを対象に、実行可否、実行をおこなうコマンド
        ReadOnly Property NormalMonitorCommand As CompositeCommand
    
        ' 登録した全コマンドのうち、【アクティブなコマンドだけ】を対象に、実行可否、実行をおこなうコマンド
        ReadOnly Property ActiveMonitorCommand As CompositeCommand
    
    End Interface
    

  •  CommonFolder.vb

  • Imports Prism.Commands
    
    Public Class CommonFolder
        Implements ICommonFolder
    
        ' 登録した全コマンドを対象に、実行可否、実行をおこなうコマンド
        Private _NormalMonitorCommand As CompositeCommand
        Public ReadOnly Property NormalMonitorCommand As CompositeCommand Implements ICommonFolder.NormalMonitorCommand
            Get
    
                If _NormalMonitorCommand Is Nothing Then
                    _NormalMonitorCommand = New CompositeCommand()
                End If
                Return _NormalMonitorCommand
    
            End Get
        End Property
    
        ' 登録した全コマンドのうち、【アクティブなコマンドだけ】を対象に、実行可否、実行をおこなうコマンド
        Private _ActiveMonitorCommand As CompositeCommand
        Public ReadOnly Property ActiveMonitorCommand As CompositeCommand Implements ICommonFolder.ActiveMonitorCommand
            Get
    
                ' アクティブなコマンドのみ監視するように設定
                If _ActiveMonitorCommand Is Nothing Then
                    _ActiveMonitorCommand = New CompositeCommand(True)
                End If
                Return _ActiveMonitorCommand
    
            End Get
        End Property
    
    End Class
    

  •  続いて、タブページ1つ分の画面を管理するプロジェクトです。同様にクラスライブラリで作成しています。

  •  TabViewModel.vb

  • Imports Prism.Mvvm
    Imports Prism.Commands
    Imports Prism.Regions
    Imports Prism.Modularity
    Imports Prism
    Imports WpfApplication1.Core
    
    Namespace ViewModels
    
        Public Class TabViewModel
            Inherits BindableBase
            Implements IActiveAware
    
            Private _Title As String
            Public Property Title() As String
                Get
                    Return _Title
                End Get
                Set(ByVal value As String)
                    Me.SetProperty(_Title, value)
                End Set
            End Property
    
            Private _IsChecked As Boolean
            Public Property IsChecked() As Boolean
                Get
                    Return _IsChecked
                End Get
                Set(ByVal value As Boolean)
                    Me.SetProperty(_IsChecked, value)
                End Set
            End Property
    
            Private _UpdateText As String
    
            Public Property UpdateText() As String
                Get
                    Return _UpdateText
                End Get
                Set(ByVal value As String)
                    Me.SetProperty(_UpdateText, value)
                End Set
            End Property
    
            Private MyFolder As ICommonFolder
            Public Property UpdateCommand As DelegateCommand
    
            ' コンストラクタ
            Public Sub New(_MyFolder As ICommonFolder)
    
                Me.UpdateCommand = New DelegateCommand(AddressOf Me.Update)
                Me.UpdateCommand.ObservesCanExecute(Function() Me.IsChecked)
    
                Me.MyFolder = _MyFolder
                Me.MyFolder.NormalMonitorCommand.RegisterCommand(Me.UpdateCommand)
                Me.MyFolder.ActiveMonitorCommand.RegisterCommand(Me.UpdateCommand)
    
            End Sub
    
            Private Sub Update()
    
                Me.UpdateText = $"Updated : {DateTime.Now}"
    
            End Sub
    
            Private _IsActive As Boolean
            Public Property IsActive As Boolean Implements IActiveAware.IsActive
                Get
                    Return Me._IsActive
                End Get
                Set(value As Boolean)
    
                    Me._IsActive = value
                    Me.UpdateCommand.IsActive = value
                    RaiseEvent IsActiveChanged(Me, New EventArgs)
    
                End Set
            End Property
    
            Public Event IsActiveChanged As EventHandler Implements IActiveAware.IsActiveChanged
    
        End Class
    
    End Namespace
    

  •  TabView.xaml
  •  画面クラスのコードビハインドには、名前空間の追加以外、処理はありません。

  • <UserControl x:Class="Views.TabView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:local="clr-namespace:ClassLibrary1.Views"
                 xmlns:prism="http://prismlibrary.com/"
                 prism:ViewModelLocator.AutoWireViewModel="True"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
    
        <!-- タブページ1つ分になる画面 -->
        <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
    
            <TextBlock Text="{Binding Title}" Margin="10" />
            <CheckBox Content="Can Execute" IsChecked="{Binding IsChecked}" Margin="10" />
            <Button Content="更新" Command="{Binding UpdateCommand}" Margin="10" />
            <TextBlock Text="{Binding UpdateText}" Margin="10" Width="200" />
    
        </StackPanel>
        
    </UserControl>
    

  •  ClassLibrary1Module.vb

  • Imports Microsoft.Practices.Unity
    Imports Prism.Regions
    Imports Prism.Modularity
    Imports ClassLibrary1.Views
    Imports ClassLibrary1.ViewModels
    
    Public Class ClassLibrary1Module
        Implements IModule
    
        Private Manager As IRegionManager
        Private Container As IUnityContainer
    
        Public Sub New(_manager As IRegionManager, _container As IUnityContainer)
    
            Me.Manager = _manager
            Me.Container = _container
    
        End Sub
    
        Public Sub Initialize() Implements IModule.Initialize
    
            ' ContentRegion 区画に、TabView をタブページとして登録
            Dim view As TabView = Nothing
            Dim items = New String() {"A", "B", "C"}
            Dim topRegion = Me.Manager.Regions("ContentRegion")
    
            For Each item In items
    
                view = Me.Container.Resolve(Of TabView)()
                CType(view.DataContext, TabViewModel).Title = $"Tab {item}"
                topRegion.Add(view)
    
            Next
    
        End Sub
    
    End Class
    

  •  最後に、メインプロジェクトです。こちらは WPF アプリケーションプロジェクトです。

  •  Window1ViewModel.vb

  • Imports Prism.Mvvm
    Imports Prism.Commands
    Imports Prism.Regions
    Imports Prism.Modularity
    Imports WpfApplication1.Core
    
    Namespace ViewModels
    
        Public Class Window1ViewModel
            Inherits BindableBase
    
            Private _Title As String = "Prism Unity Application"
            Public Property Title() As String
                Get
                    Return _Title
                End Get
                Set(ByVal value As String)
                    Me.SetProperty(_Title, value)
                End Set
            End Property
    
            Private MyFolder As ICommonFolder
            Public Property AllCommand As CompositeCommand
            Public Property ActiveCommand As CompositeCommand
    
            Public Sub New(_MyFolder As ICommonFolder)
    
                Me.MyFolder = _MyFolder
                Me.AllCommand = Me.MyFolder.NormalMonitorCommand
                Me.ActiveCommand = Me.MyFolder.ActiveMonitorCommand
    
            End Sub
    
        End Class
    
    End Namespace
    

  •  Window1.xaml
  •  画面クラスのコードビハインドには、名前空間の追加以外、処理はありません。

  • <Window x:Class="Views.Window1"
            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:local="clr-namespace:WpfApplication1.Views"
            xmlns:prism="http://prismlibrary.com/"
            prism:ViewModelLocator.AutoWireViewModel="True"
            mc:Ignorable="d"
            Title="{Binding Title}" Height="350" Width="525">
    
        <Window.Resources>
            <Style TargetType="TabItem">
                <Setter Property="Header" Value="{Binding DataContext.Title}" />
            </Style>
        </Window.Resources>
        
        <StackPanel>
    
            <StackPanel Margin="20">
                
                <Button Content="全ボタンを対象に実行" Command="{Binding AllCommand}" />
                <Button Content="表示中のボタンを対象に実行" Command="{Binding ActiveCommand}" />
                <TabControl prism:RegionManager.RegionName="ContentRegion" />
    
            </StackPanel>
            
        </StackPanel>
    
    </Window>
    

  •  Bootstrapper.vb

  • Imports Microsoft.Practices.Unity
    Imports Prism.Unity
    Imports Prism.Modularity
    Imports WpfApplication1.Views
    Imports WpfApplication1.Core
    Imports ClassLibrary1
    
    Public Class Bootstrapper
        Inherits UnityBootstrapper
    
        ' Shell 担当になる View のインスタンスを返却
        Protected Overrides Function CreateShell() As DependencyObject
            Return Me.Container.Resolve(Of Window1)
        End Function
    
        ' Shell 担当になる View を表示
        Protected Overrides Sub InitializeShell()
            Application.Current.MainWindow.Show()
        End Sub
    
        Protected Overrides Sub ConfigureContainer()
            MyBase.ConfigureContainer()
    
            ' 各クラスのインスタンス時、
            ' ICommonFolder インターフェース(インスタンスは、CommonFolder クラス)をインジェクションしたいため、
            ' シングルトン形式で登録
            Me.Container.RegisterType(Of ICommonFolder, CommonFolder)(New ContainerControlledLifetimeManager)
    
        End Sub
    
        Protected Overrides Sub ConfigureModuleCatalog()
    
            ' モジュールカタログに、ClassLibrary1 プロジェクトに関する Module を登録
            Dim catalog = CType(Me.ModuleCatalog, ModuleCatalog)
            catalog.AddModule(GetType(ClassLibrary1Module))
    
        End Sub
    
    End Class
    

  •  Application.xaml.vb
  •  ※Application.xaml では、【StartupUri="MainWindow.xaml"】を削除しています。

  • Class Application
    
        ' Startup、Exit、DispatcherUnhandledException などのアプリケーション レベルのイベントは、
        ' このファイルで処理できます。
    
        Protected Overrides Sub OnStartup(e As StartupEventArgs)
            MyBase.OnStartup(e)
    
            ' Unity 管理による Prism アプリケーションの起動制御処理を実行
            Dim boot = New Bootstrapper
            boot.Run()
    
        End Sub
    
    End Class