VB のたまご

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



StackPanelに画面インジェクション

  •  要件によっては、ContentControl を継承していない StackPanel 内に画面をインジェクションしたいケースも出てくるかと思います。

  • <Window x:Class="Views.Window1"xmlns:prism="http://prismlibrary.com/"
            prism:ViewModelLocator.AutoWireViewModel="True"
            ~>
    
        <Grid>
    
            <StackPanel prism:RegionManager.RegionName="ContentRegion" />
    
        </Grid>
        
    </Window>
    

    スポンサーリンク


  •  このような場合は、自前で解決ルールを記載したアダプターを作成して、RegionManager に登録することで、 画面インジェクションすることができるようになります。 前回出てきた、SubView ユーザーコントロールと、Window1ViewModel クラスは全く修正無しでいけるところに注目です。

  •  以下は、プロジェクト配下に Prisms フォルダを作成して、その中に作成したクラスです。 難しそうに見えますが、肝心なところは、Adapt メソッド内で、インジェクションしたい画面を、StackPanel の Children プロパティに追加しているところです。

  • Imports Prism.Regions
    Imports System.Collections.Specialized
    
    ' 通常、区画を割り当てるのは ContentControl クラスですが、
    ' ContentControl を継承していない StackPanel などのクラスにも、区画を割り当てられるようにするアダプタークラスを作成します。
    Namespace Prisms
    
        Public Class StackPanelRegionAdapter
            Inherits RegionAdapterBase(Of StackPanel)
    
            Public Sub New(regionBehaviorFactory As IRegionBehaviorFactory)
                MyBase.New(regionBehaviorFactory)
            End Sub
    
            Protected Overrides Sub Adapt(region As IRegion, regionTarget As StackPanel)
    
                ' region.Views.CollectionChanged イベントに対応するイベントハンドラを作成
                Dim handler = Sub(s As Object, e As NotifyCollectionChangedEventArgs)
    
                                  If e.Action = NotifyCollectionChangedAction.Add Then
    
                                      For Each element As FrameworkElement In e.NewItems
                                          regionTarget.Children.Add(element)
                                      Next
    
                                  End If
    
                              End Sub
    
                AddHandler region.Views.CollectionChanged, handler
    
            End Sub
    
            Protected Overrides Function CreateRegion() As IRegion
                Return New AllActiveRegion()
            End Function
    
        End Class
    
    End Namespace
    

  •  アダプタークラスを作成したら、RegionManager に登録します。 具体的にはブートストラッパークラス内に、以下を追加します。

  • Protected Overrides Function ConfigureRegionAdapterMappings() As RegionAdapterMappings
    
        Dim mappings = MyBase.ConfigureRegionAdapterMappings()
        mappings.RegisterMapping(GetType(StackPanel), Me.Container.Resolve(Of StackPanelRegionAdapter)())
        Return mappings
    
    End Function
    

  •  後は前回と同じものです。 このサンプルを実行すると、Window1 画面に SubView ユーザーコントロールが表示されて、かつ StackPanel 内なので画面上部に表示されています。

  • スポンサーリンク


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

  •  StackPanelRegionAdapter.vb

  • Imports Prism.Regions
    Imports System.Collections.Specialized
    
    ' 通常、区画を割り当てるのは ContentControl クラスですが、
    ' ContentControl を継承していない StackPanel などのクラスにも、区画を割り当てられるようにするアダプタークラスを作成します。
    Namespace Prisms
    
        Public Class StackPanelRegionAdapter
            Inherits RegionAdapterBase(Of StackPanel)
    
            Public Sub New(regionBehaviorFactory As IRegionBehaviorFactory)
                MyBase.New(regionBehaviorFactory)
            End Sub
    
            Protected Overrides Sub Adapt(region As IRegion, regionTarget As StackPanel)
    
                ' region.Views.CollectionChanged イベントに対応するイベントハンドラを作成
                Dim handler = Sub(s As Object, e As NotifyCollectionChangedEventArgs)
    
                                  If e.Action = NotifyCollectionChangedAction.Add Then
    
                                      For Each element As FrameworkElement In e.NewItems
                                          regionTarget.Children.Add(element)
                                      Next
    
                                  End If
    
                              End Sub
    
                AddHandler region.Views.CollectionChanged, handler
    
            End Sub
    
            Protected Overrides Function CreateRegion() As IRegion
                Return New AllActiveRegion()
            End Function
    
        End Class
    
    End Namespace
    

  •  Window1ViewModel.vb

  • Imports Prism.Regions
    Imports Prism.Unity
    Imports Microsoft.Practices.Unity
    
    Namespace ViewModels ' ←名前空間を追加する
    
        Public Class Window1ViewModel
    
            Public Property Title As String = "Prism Unity Application"
    
            ' Unity コンテナ経由でインスタンス生成されてくるため、コンストラクタの引数にしている、
            ' IRegionManager と IUnityContainer インターフェースには、RegionManager と UnityContainer クラスのインスタンスが渡ってくる
            Public Sub New(manager As IRegionManager, container As IUnityContainer)
    
                ' ContentRegion 区画に、SubView ユーザーコントロールを登録(インジェクション)する
                manager.RegisterViewWithRegion("ContentRegion", Function() container.Resolve(Of Object)("SubView"))
    
            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"
            xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
            mc:Ignorable="d"
            Title="{Binding Title}" Height="350" Width="525">
    
        <Grid>
    
            <StackPanel prism:RegionManager.RegionName="ContentRegion" />
    
        </Grid>
        
    </Window>
    

  •  SubView.xaml

  • <UserControl x:Class="Views.SubView"
                 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:WpfApplication1.Views"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        
        <Grid>
    
            <TextBox Text="Hello, Prism! from UserControl" />
    
        </Grid>
        
    </UserControl>
    

  •  Bootstrapper.vb

  • Imports Microsoft.Practices.Unity
    Imports Prism.Unity
    Imports Prism.Mvvm
    Imports Prism.Regions
    Imports WpfApplication1.Views
    Imports WpfApplication1.Prisms
    
    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
    
        ' Unity コンテナの設定
        Protected Overrides Sub ConfigureContainer()
            MyBase.ConfigureContainer()
    
            ' ViewModel から指定できるように、Object 型の SubView 名で登録しておく
            Me.Container.RegisterType(Of Object, SubView)("SubView")
    
        End Sub
    
        ' 区画対象のコントロールとアダプターの紐づけ設定
        Protected Overrides Function ConfigureRegionAdapterMappings() As RegionAdapterMappings
    
            Dim mappings = MyBase.ConfigureRegionAdapterMappings()
            mappings.RegisterMapping(GetType(StackPanel), Me.Container.Resolve(Of StackPanelRegionAdapter)())
            Return mappings
    
        End Function
    
    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