IObservableインターフェイス
IObservable(T) インターフェイス (System)
IObserver(T) インターフェイス (System)
.NET Framework 4から追加されたこれらのインターフェイスを使ったサンプルを作成していました。
オブザーバーパターンは一方通行の通知を行うパターンだというのに、チャットアプリケーションを作ってしまったため
クライアントウィンドウがごちゃごちゃしています。
配信側、受信側クラス
public class ChatHost : IObservable<ChatMessage> { protected List<IObserver<ChatMessage>> observers = new List<IObserver<ChatMessage>>(); public ChatHost() { } public IDisposable Subscribe(IObserver<ChatMessage> observer) { observers.Add(observer); return new _ChatMessage(this, observer); } private class _ChatMessage : IDisposable { private ChatHost parent; private IObserver<ChatMessage> me; public _ChatMessage(ChatHost parent, IObserver<ChatMessage> me) { this.parent = parent; this.me = me; } public void Dispose() { if (parent != null && parent.observers.Contains(me)) parent.observers.Remove(me); } } public void AddMessage(ChatMessage msg) { if (msg == null) { foreach (var o in observers) { o.OnError(new ChatException() { Msg = new ChatMessage() { From = "SYSTEM", Message = "無言メッセージが送信されました。送ったのは誰だ!!", SendAt = DateTime.Now } }); } return; } foreach (var o in observers) { o.OnNext(msg); } } public void EndChat() { foreach (var o in observers) { o.OnNext(new ChatMessage() { From = "SYSTEM", Message = "今日はお開きです!!", SendAt = DateTime.Now }); } int obsCount = observers.Count; for (int i = obsCount - 1; i >= 0; i--) { observers[i].OnCompleted(); } } } public class ChatException : Exception { public ChatMessage Msg { get; set; } public ChatException() : base() { } } public class ChatClient : IObserver<ChatMessage> { private IDisposable unsubscriber; private ObservableCollection<ChatMessage> msgItemList; public ChatClient(ObservableCollection<ChatMessage> msgItemList) { this.msgItemList = msgItemList; } public virtual void Subscribe(IObservable<ChatMessage> provider) { if (provider != null) unsubscriber = provider.Subscribe(this); } public void OnCompleted() { if(unsubscriber != null) unsubscriber.Dispose(); } public void OnError(Exception error) { var charError = error as ChatException; if (charError == null) return; msgItemList.Add(charError.Msg); } public void OnNext(ChatMessage value) { msgItemList.Add(new ChatMessage() { From = value.From, Message = value.Message, SendAt = value.SendAt }); } }
メインウィンドウ。クライアント画面表示やチャットセッションの切断をおこなう。
<Window x:Class="ObservableSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <StackPanel> <Button x:Name="btnAddClient" Click="btnAddClient_Click" Margin="10">クライアント追加</Button> <Button x:Name="btnEnd" Margin="10" Click="btnEnd_Click">終了</Button> </StackPanel> </Window>
public partial class MainWindow : Window { private ChatHost provider = new ChatHost(); public MainWindow() { InitializeComponent(); } private void btnAddClient_Click(object sender, RoutedEventArgs e) { var window = new Window1(provider); window.Show(); } private void btnEnd_Click(object sender, RoutedEventArgs e) { provider.EndChat(); } }
クライアントウィンドウ。
<Window x:Class="ObservableSample.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="356" Width="518" Loaded="Window_Loaded" Closed="Window_Closed"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="8*" /> <RowDefinition Height="2*" /> </Grid.RowDefinitions> <ListView Grid.Row="0" x:Name="listview1"> <ListView.View> <GridView> <GridViewColumn DisplayMemberBinding="{Binding Path=From}" Header="送信者" Width="100" /> <GridViewColumn DisplayMemberBinding="{Binding Path=Message}" Header="メッセージ" Width="150" /> <GridViewColumn DisplayMemberBinding="{Binding Path=SendAt}" Header="日付" Width="200" /> </GridView> </ListView.View> </ListView> <StackPanel Orientation="Horizontal" Grid.Row="1"> <TextBox x:Name="textbox1" Width="300" Margin="5"></TextBox> <Button x:Name="button1" Width="100" Margin="5" Click="button1_Click">送信</Button> </StackPanel> </Grid> </Window>
public partial class Window1 : Window { private ObservableCollection<ChatMessage> msgs = new ObservableCollection<ChatMessage>(); private ChatHost provider; private ChatClient observer; private string userName = ""; public Window1() { InitializeComponent(); } public Window1(ChatHost provider) { this.provider = provider; this.observer = new ChatClient(msgs); observer.Subscribe(provider); InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { listview1.ItemsSource = msgs; //ユーザー名入力ダイアログを表示、ユーザー名設定。 var dialog = new Window2(); dialog.Owner = this; dialog.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner; dialog.ShowInTaskbar = false; dialog.ShowDialog(); this.userName = dialog.UserName; this.Title = userName; } private void button1_Click(object sender, RoutedEventArgs e) { if (textbox1.Text.Trim().Length == 0) { provider.AddMessage(null); } else { provider.AddMessage(new ChatMessage() { From = userName, Message = textbox1.Text, SendAt = DateTime.Now }); } } private void Window_Closed(object sender, EventArgs e) { observer.OnCompleted(); } }