for WPF developers
ここでは YKToolkit 開発にまつわる日記的なものを掲載しています。 いわば自分用メモみたいなものもあれば、「こんなテク使ったよ」的なことも載せます。
唐突に更新された YKToolkit.Controls.dll Ver.1.11.0.0 ですが、 ParentPanel および ChildWindow コントロールがなんの説明もなく追加されています。 これらは子ウィンドウを実現するためのコントロールです。 さすがに誰も使えないだろうと思うので、 とりあえずこちらにサンプルコードを載せておきます。ああ、説明書も更新しなきゃ...
そもそも子ウィンドウとはなんなのか。
Excel や Word なんかで複数のファイルを開いたとき、
それぞれのファイルに対してウィンドウが与えられますよね。
そのウィンドウは Excel 本体のウィンドウの中であれば自由に移動できるし、リサイズもできます。
そんなウィンドウをここでは子ウィンドウと呼んでいます。
こういう子ウィンドウを WPF でもできないかな〜なんて思ってしまったのがきっかけで、 ただなんとなく作ってしまったのが今回の更新に含まれています。 そんなわけでかなり中途半端かつオレオレコントロールになっています。
とにかく子ウィンドウを表示したいんだい!という場合は Canvas コントロールの上に
ChildWindow コントロールを配置します。
<YK:Window x:Class="WpfApplication1.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:YK="clr-namespace:YKToolkit.Controls;assembly=YKToolkit.Controls"
Title="MainView" Height="400" Width="500">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="ファイル (_F)" />
<MenuItem Header="編集 (_E)" />
<MenuItem Header="オプション (_O)" />
<MenuItem Header="ヘルプ (_H)" />
</Menu>
<Canvas>
<YK:ChildWindow Title="child1">
<TextBlock Text="ひと〜つ人の世を憂い" />
</YK:ChildWindow>
<YK:ChildWindow Title="child2">
<TextBlock Text="ふたつ不埒な悪行三昧" />
</YK:ChildWindow>
<YK:ChildWindow Title="child3">
<TextBlock Text="みっつ見てみぬふり" />
</YK:ChildWindow>
</Canvas>
</DockPanel>
</YK:Window>
閉じるボタンが効かない現象についてなんのフォローもなく、唐突に ParentPanel を導入します。
ParentPanel は IChildWindow インターフェースを実装したクラスを対象とした ItemsSource プロパティを持っています。
というわけでまずサンプルとして次のような Person クラスを用意します。
namespace WpfApplication1.Models
{
using System;
using YKToolkit.Controls;
public class Person : IChildWindow
{
#region IChildWindow のメンバ
/// <summary>
/// 子ウィンドウとしてのアクティブ状態を取得または設定します。
/// </summary>
public bool IsActive { get; set; }
private bool isClosed;
/// <summary>
/// 子ウィンドウが閉じられたかどうかを確認または設定します。
/// </summary>
public bool IsClosed
{
get { return isClosed; }
set
{
if (isClosed != value)
{
isClosed = value;
if (isClosed)
RaiseClosed();
}
}
}
/// <summary>
/// 子ウィンドウのタイトルを取得または設定します。
/// </summary>
public string Title { get; set; }
#endregion IChildWindow のメンバ
#region イベント定義
/// <summary>
/// 子ウィンドウがとじられたときに発生します。
/// </summary>
public event EventHandler<EventArgs> Closed;
/// <summary>
/// Closed イベントを発行します。
/// </summary>
private void RaiseClosed()
{
var h = Closed;
if (h != null)
h(this, EventArgs.Empty);
}
#endregion イベント定義
/// <summary>
/// 名前を取得または設定します。
/// </summary>
public string Name { get; set; }
/// <summary>
/// 年齢を取得または設定します。
/// </summary>
public int Age { get; set; }
}
}
次にこれを扱う ViewModel 側のコードです。
namespace WpfApplication1.ViewModels
{
using YKToolkit.Bindings;
using WpfApplication1.Models;
using System.Collections.ObjectModel;
public class MainViewModel : NotificationObject
{
private ObservableCollection<Person> people = new ObservableCollection<Person>();
/// <summary>
/// 人物データを取得または設定します。
/// </summary>
public ObservableCollection<Person> People
{
get { return people; }
set { SetProperty(ref people, value); }
}
private DelegateCommand addCommand;
/// <summary>
/// 人物データ追加コマンドを取得します。
/// </summary>
public DelegateCommand AddCommand
{
get
{
return addCommand ?? (addCommand = new DelegateCommand(_ => AddPerson()));
}
}
/// <summary>
/// 人物データを追加します。
/// </summary>
private void AddPerson()
{
var person = new Person
{
Title = "人物データ " + People.Count.ToString(),
Name = "田中" + People.Count.ToString() + "子",
Age = 16 + People.Count,
};
person.Closed += OnPersonClosed;
People.Add(person);
}
/// <summary>
/// 人物データクローズイベントハンドラ
/// </summary>
/// <param name="sender">イベント発行元</param>
/// <param name="e">イベント引数</param>
private void OnPersonClosed(object sender, System.EventArgs e)
{
var person = sender as Person;
if (person != null)
{
person.Closed -= OnPersonClosed;
People.Remove(person);
}
}
}
}
そして有無を言わさず View の XAML を次のように書きます。
<YK:Window x:Class="WpfApplication1.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:YK="clr-namespace:YKToolkit.Controls;assembly=YKToolkit.Controls"
Title="MainView" Height="400" Width="500">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="ファイル (_F)" />
<MenuItem Header="編集 (_E)" />
<MenuItem Header="オプション (_O)" />
<MenuItem Header="ヘルプ (_H)" />
</Menu>
<Button Content="Add Person" DockPanel.Dock="Top" Command="{Binding AddCommand}" />
<YK:ParentPanel ItemsSource="{Binding People}">
<YK:ParentPanel.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name, StringFormat='{}氏名 : {0}'}" />
<TextBlock Text="{Binding Age, StringFormat='{}年齢 : {0}'}" />
</StackPanel>
</DataTemplate>
</YK:ParentPanel.ItemTemplate>
</YK:ParentPanel>
</DockPanel>
</YK:Window>
そんなわけで実行結果がこちら。
以上子ウィンドウ表示のための ParentPanel および ChildWindow コントロールでした... で終わるのもアレなので、少し雑談を。
そもそも子ウィンドウを WPF で実現させようと思ったきっかけは、冒頭でも書いたように、 Excel や Word なんかで見るいわゆる MDI 形式なアプリケーションを使っていた時です。 WPF は確か公式でも MDI はサポートしないよと言われてしまったフレームワークですが、 別にできないよとは言われていません。 つまり、サポートされなければ自作すればいいじゃないというスタンスです。 というわけで MDI 形式に耐えられる枠組みを考えようかと。 そして真っ先に手を付けられるのがこの子ウィンドウの実現でした。 そもそもこれができないと MDI の枠組みを考えたところで無意味ですし。 で、とりあえずそれっぽいものができて嬉しくなったのでついうっかりライブラリに追加してしまいました。反省。
MDI として機能させるためには何が必要か。
こうして考えてみると後は MDI として作り込むための雛形があればもうできるんじゃないか。
というわけでやってみる ... 時間はないので適当に作ったバイナリエディタのスクショを投下して逃げます。
MDI を完成させるのは実業務でない限りやる気も気合いも出ないなぁ。
Designed by CSS.Design Sample