げんさん日記

プログラミングで気付いた事等を書きます。

ViewModelによるユーザーコントロールの切り替え方法

2019年09月03日 16時21分58秒 | WPF
Viewmodel内でパネルの内容を動的に切り替える方法を記します。
方法としてはDataTemplateを定義して切り替えます。
手順は下記の通りです。

App.XamlにViewとViewModelのマッチングをApplicationのリソースに定義します。
<Application.Resources>
  <DataTemplate DataType="{x:Type local:Panel1ViewModel}">
    <local:Panel1View/>
  </DataTemplate>
  <DataTemplate DataType="{x:Type local:Panel2ViewModel}">
    <local:Panel2View/>
  </DataTemplate>
</Application.Resources>

XamlにContentControlを配置する。
<ContentControl Content="{Binding ViewModel}"/>

ViewModelで表示するViewModelを設定する。
public class MainViewModel : BaseViewModel
{
  // 表示させる画面のViewModel
  public BaseViewModel ViewModel
  {
    get { return base.Get<BaseViewModel >(); }
    set { base.Set(value);
  }

  public MainViewModel(int no)
  {
    if (no == 0)
    {
      this.ViewModel = new Panel1ViewModel();
    }
    else
    {
      this.ViewModel = new Panel2ViewModel();
    }
  }
}


ViewModelからViewを表示する

2019年09月03日 13時27分34秒 | WPF
Livet及びPrism等のフレームワークを使用した場合、メッセンジャー形式の為、ViewModelからViewを表示した場合の戻り値がイベントとなり、ソースが見にくくなります。ViewModelのみで完結する方法として下記ViewとViewModelのマッチングする方法を記述します。
作成意図としてはViewModelからViewを表示し戻り値を次の行で受け取る仕組みにすることでPGの可読性を高くする。

ViewとViewModelのマッチングDictionaryをAppに作成する。
public partial class App : Application
{
  private Dictionary ViewModelMatchings { get; set; } = new Dictionary<Type,Type>();
}

コンストラクタでViewとViewModelのマッチングを定義する。
public partial class App : Application
{
  public App()
  {
    ViewModelMatchings.Add(typeof(MainView),typeof(MainViewModel));
    ViewModelMatchings.Add(typeof(SubView),typeof(SubviewModel));
  }
}

ViewModelBaseで画面表示処理を行う。
public class ViewModelBase
{
  // ViewModelからViewを生成する
  public IVewResult ShowDialog(T viewModel)
  {
    // ViewModelに対応するViewを検索する
    var ret = ViewModelMatchings.ContainsKey(viewModel.GetType());
    if (ret = true)
    {
      // Viewを生成し、DataContextにViewModelを設定する
      var viewType = ViewModelMatchings[typeof(viewModel)];
      var view = Activator.CreateInstance(viewType) as Window;
      view.DataContext = viewModel;
      view.ShowDialog();
      return view.DataContext.Result as IViewResult;
    }
  }
}

使用例
public class MainViewModel : ViewModelBase
{
  // サブ画面表示
  public void ShowSubWindow()
  {
    using (var vm = new SubViewModel())
    {
      var ret = base.ShowDialog(vm);
      // retの情報により処理を分けられる。
    }
  }
}
上記のメリットは画面表示後の処理をViewModelで連続して処理を行うことができます。

Windowがアクティブな時のみ枠が表示される。

2019年09月03日 10時06分40秒 | WPF
Xamlのリソースにボーダーのスタイルを作成する。
 <Window.Resources>
    <Style x:Key="ActiveBorderStyle" TargetType="{x:Type Border}"> 
    </Style>
</Window.Resources>   

アクティブ時のボーダー色をBlueVioletで表示する。
 <Setter Property="BorderBrush" Value="BlueViolet" />
 <Setter Property="BorderThickness" Value="1" />  

非アクティブ時のボーダー色をWindowのBackgroundと同一にする。
 <Style.Triggers>
    <DataTrigger Binding="{Binding ElementName=BaseWindow, Path=IsActive}" Value="false">
        <Setter Property="BorderBrush" Value="{Binding ElementName=BaseWindow, Path=Background}" />
    </DataTrigger>
 </Style.Triggers> 

最終的に下記のようになります。
<Window.Resources>
  <Style x:Key="ActiveBorderStyle" TargetType="{x:Type Border}">
    <Setter Property="BorderBrush" Value="BlueViolet" />
    <Setter Property="BorderThickness" Value="1" />
    <Style.Triggers>
      <DataTrigger Binding="{Binding ElementName=BaseWindow, Path=IsActive}" Value="false">
        <Setter Property="BorderBrush" Value="{Binding ElementName=BaseWindow, Path=Background}" />
      </DataTrigger>
    </Style.Triggers> 
  </Style>
</Window.Resources>
<Border Style="{StaticResource ActiveBorderStyle}"/>
  <Grid>
    ・・・・
  </Grid>
</Border>

実行例