RunningCSharp

MS系開発者による、雑多な記事。記事は所属企業とは関係のない、個人の見解です。

WPF:配列からRadioButtonを自動生成するItemsControlを作成

タイトルの通りItemsSourceにboolとstringを持ったオブジェクトの配列を渡すことでRadioButtonを生成するコントロールのサンプルです。

サンプルはGitHubにアップしてみました。 github.com

ItemsControlを継承したカスタムコントロールとなっております、利用時にはResourceDictionaryの読み込みが必要な感じになっています。ResourceDictionaryたいしたことは書いていませんが、改善で拡張する際にあったほうが便利かと思いつけました。

一応、使い方を。 ①RadioButtonList.csとRadioButtonList.xamlをプロジェクトに追加

②利用するView(ウィンドウなど)またはApp.xamlなど参照可能な場所に

<ResourceDictionary Source="/radiobuttonlist;component/RadioButtonList.xaml"/>

上記のリソースを読み込んだうえで、

<ctl:RadioButtonList xmlns:ctl="clr-namespace:radiobuttonlist"
ItemsSource="{Binding Items}" CheckedItem="{Binding CheckedItem}"
TitleMemberPath="Title" IsCheckedMemberPath="IsChecked"/>

上記のような感じのタグで配置します。

③ViewModel側ではItemsSourceに指定した配列クラスの「IsCheckプロパティにバインドしたいプロパティ名」を「IsCheckedMemberPath」に、 「キャプションとして表示したい文字列のプロパティ名」を「IsCheckedMemberPath」に指定します。

こんな感じで使えるかと。

例えば、上記のタグに下記のようなデータクラスをバインドした場合、

//ItemsはItemsSourceプロパティとバインドされたObservableCollection
Items.Add(new RadioButtonItem() { IsChecked = true, Title = "test1" });
Items.Add(new RadioButtonItem() { IsChecked = false, Title = "test2" });
Items.Add(new RadioButtonItem() { IsChecked = false, Title = "test3" });
Items.Add(new RadioButtonItem() { IsChecked = false, Title = "test4" });
Items.Add(new RadioButtonItem() { IsChecked = false, Title = "test5" });

下記のような画面が表示されます。

f:id:ys-soniclab:20160730230748p:plain

また、IsCheckedプロパティがTrueになったオブジェクトはCheckedItemプロパティに格納されます。


作成時、ItemTempleteの動的生成部分はちょっと作るのが大変でした。

//ItemTemplateをプロパティ値から動的に作成
var elementFactory = new FrameworkElementFactory(typeof(RadioButton));
if (!string.IsNullOrEmpty(TitleMemberPath))
{
    elementFactory.SetValue(RadioButton.ContentProperty, new Binding(TitleMemberPath));
}
if (!string.IsNullOrEmpty(IsCheckedMemberPath))
{
    elementFactory.SetValue(RadioButton.IsCheckedProperty, new Binding(IsCheckedMemberPath));
}
elementFactory.SetValue(RadioButton.GroupNameProperty, string.IsNullOrEmpty(GroupName) ? Guid.NewGuid().ToString() : GroupName);
elementFactory.AddHandler(RadioButton.CheckedEvent, new RoutedEventHandler(CheckedMethod));
this.ItemTemplate = new DataTemplate { VisualTree = elementFactory };

書きづらいし読みづらいとも思いますが、Bindingオブジェクトも色々手を付けられ、イベントに対応したメソッドも簡単に取り付けられるのは便利だと思いました。