Calender

S M T W T F S
 123456
78910111213
14151617181920
21222324252627
28293031   
<< October 2018 >>

Categories

Archives

Recent Entries

Recent Comment

Recent Trackback

w closet×JUGEM

-

WPF で画面とユーザーコントロールの連携を考えてみた - 周回遅れのブルース

@media print { body { margin: 2mm 9mm; } .original-url { display: none; } #article .float.left { float: left !important; } #article .float.right { float: right !important; } #article .float { margin-top: 0 !important; margin-bottom: 0 !important; } } WPF で画面とユーザーコントロールの連携を考えてみた - 周回遅れのブルース

WPF で画面とユーザーコントロールの連携を考えてみた

.NET, WPF, MVVM, Livet | 14:19 | wpf で画面とユーザーコントロールの連携を考えてみたを含むブックマーク

ちょっと某所で質疑があがってたので、MVVMパターンにおける画面とユーザーコントロールの連携について考えてみました。

お題をかいつまんで言えば、こんな話です。

メインウィンドウと二つのユーザーコントロールが存在し、それぞれプロジェクトを分けて管理したい。画面に各ユーザーコントロールを貼り付けてコントロール間で連携を取りたいが、コントロール同士がお互い参照しない様に実装したい。どうすればいいか?


ぱっと頭の中で浮かんだのが、メインウィンドウと二つのユーザーコントロール各々に ViewModel を設け、メインウィンドウの ViewMdeol に各コントロールの ViewModel を管理させればいいじゃんと。でも説明だけじゃイメージ湧かないかも知れないので、少しサンプルを考えてみました。ちなみに MVVMインフラは Livet を使います。


まずコントロール側の View と ViewModel。話を単純にするためコントロールの UI は TextBox だけにしてみます。まずユーザーコントロールA。「WPFユーザーコントロールライブラリ」で新規作成します。

<UserControl x:Class="UserControlA.UserControlA"
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:UserControlA"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<local:ViewModelA />
</UserControl.DataContext>
<TextBox BorderBrush="Red" BorderThickness="3" Margin="8"
Text="{Binding Message, UpdateSourceTrigger=PropertyChanged}"/>
</UserControl>
using Livet;
namespace UserControlA {
public class ViewModelA : ViewModel {
private string _Message;
public string Message {
get { return _Message; }
set {
if (_Message == value) return;
_Message = value;
RaisePropertyChanged("Message");
}
}
}
}

お次にユーザーコントロールB。これも「WPFユーザーコントロールライブラリ」で新規作成し、ユーザーコントロールA とほぼ同じ仕様で作ります。違うのは Border の色だけ。

<UserControl x:Class="UserControlB.UserControlB"
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:UserControlB"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<local:ViewModelB />
</UserControl.DataContext>
<TextBox BorderBrush="Blue" BorderThickness="3" Margin="8"
Text="{Binding Message, UpdateSourceTrigger=PropertyChanged}"/>
</UserControl>
using Livet;
namespace UserControlB {
public class ViewModelB : ViewModel {
private string _Message;
public string Message {
get { return _Message; }
set {
if (_Message == value) return;
_Message = value;
RaisePropertyChanged("Message");
}
}
}
}

最後にメインウィンドウ。まず画面の ViewModel に、各コントロールの ViewModel のインスタンスを管理するためのプロパティを用意します。二つの ViewModel のインスタンスが設定されたら、プロパティを連携するようイベントリスナーを登録します。余談ですが、ここでは通常のイベントリスナーである EventListener を登録してますが、メモリリーク対策を強化したいなら、弱参照用に用意された LivetWeakEventListener を使えばいいと思います。

using Livet;
using Livet.EventListeners;
using System.ComponentModel;
using UserControlA;
using UserControlB;
namespace Sample {
public class MainViewModel : ViewModel {
private ViewModelA _VmA;
public ViewModelA VmA {
get { return _VmA; }
set {
if (_VmA == value) return;
_VmA = value;
RaisePropertyChanged("ViewModelA");
this.AddListener();
}
}
private ViewModelB _VmB;
public ViewModelB VmB {
get { return _VmB; }
set {
if (_VmB == value) return;
_VmB = value;
RaisePropertyChanged("ViewModelB");
this.AddListener();
}
}
private void AddListener() {
if (this.VmA == null || this.VmB == null) return;
this.CompositeDisposable.Add(
new EventListener<PropertyChangedEventHandler>(
h => this.VmA.PropertyChanged += h,
h => this.VmA.PropertyChanged -= h,
(sender, e) => {
if (e.PropertyName == "Message") {
this.VmB.Message = this.VmA.Message;
}
}
)
);
}
}
}

最後にメインウィンドウの XAML。View のコードビハインドのコンストラクタ内で、各コントロールのViewModel を設定します。

<Window x:Class="Sample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Sample"
xmlns:UserControlA="clr-namespace:UserControlA;assembly=UserControlA"
xmlns:UserControlB="clr-namespace:UserControlB;assembly=UserControlB"
Title="MainWindow" Height="200" Width="400">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<UserControlA:UserControlA x:Name="A" Grid.Column="0" Height="50" />
<UserControlB:UserControlB x:Name="B" Grid.Column="1
  • 2018.08.06 Monday
  • 10:26

Comment
Send Comment








   
この記事のトラックバックURL
Trackback