trapemiyaの日記

hatenablogが新しくなったんで新規一転また2019年1月からちょこちょこ書いてます。C#中心のプログラミングに関するお話です。

Windows Update後、SharePoint 2010で「このWebパーツを表示できません」と表示されるようになった。

結局、KB2844286が犯人だったようで、これをアンインストールしたところ、見事に復活!
以下のサイトに多謝。

“Unable to display this Web Part” error message after patch KB2844286
http://social.technet.microsoft.com/Forums/sharepoint/en-US/cc9a557b-93cd-40d5-965c-e0a2f107624d/unable-to-display-this-web-part-error-message-after-patch-kb2844286
ISSUE: KB2844286 Security Update on SharePoint 2010
http://techchucker.wordpress.com/tag/kb2844286/

CollectionViewSourceを使ったグルーピングあれこれ【その3 GroupNameFromItemオーバーライド時の問題解決編】

前回の、

CollectionViewSourceを使ったグルーピングあれこれ【その2 GroupNameFromItemオーバーライド時の問題】
http://d.hatena.ne.jp/trapemiya/?_ts=1344491065

の続きです。

結局、GroupNameFromItemをオーバーライドして返される値でグルーピングされてしまうので、GroupNameFromItemを使う限りどうしようもないことがわかります。まぁ、その値を例えば、「ランナー名【ランナーID】」のようにすればほぼ間違いなくユニークになりますが、これだと負けのような気がしますし、第一、ランナー名だけ表示したい場合には太刀打ちできません。
よって、GroupNameFromItemをオーバーライドすることはきっぱり止めにします。

では、どうするか? まずはグルーピングされたヘッダに何がバインドしているのかを知らなければ糸口が見つかりません。調べてみると、CollectionViewGroupクラスがバインドしていることがわかります。

CollectionViewGroup クラス
http://msdn.microsoft.com/ja-jp/library/system.windows.data.collectionviewgroup.aspx

このクラスのメンバーを見るとわかるのですが、その数がものすごく少ないんですね。その中にNameプロパティという重要なプロパティがあって、ここにグループヘッダの表示に使用できる値が入るようになっています。GroupNameFromItemをオーバーライドした場合、そこから返される値がNameプロパティにセットされます。なので、以下のようにNameプロパティにバインドさせているわけです。

<DataTemplate x:Key="グループヘッダTemplate">
    <StackPanel Background="#FFCDF2CD">
        <TextBlock Text="{Binding Name}" FontWeight="Bold"/>
        <Rectangle Height="1" Fill="Blue"/>
    </StackPanel>
</DataTemplate>

ここで閃きませんか?閃きますよね。バインドされている値を変えたいならコンバーターを使え!と。
さて、それは良いとして、CollectionViewGroupクラスの何をバインドさせましょうか?Nameプロパティにはグルーピングで指定したランナーIDがセットされています。(GroupNameFromItemのオーバーライドを止めましたからね) というわけでNameプロパティをバインドしても意味がありません。そこで、ランナー名が含まれているプロパティを探すと・・・・・Itemsしか無さそうです。このコレクションに含まれるランナー名は全て同じですし、Itemsには最低1つの要素が存在しますから、Itemsの最初の1個のみをバインドすれば良さそうです。コンバーターの名前は「ランナー名取得Converter」として、以下のようにグループヘッダTemplateを変更します。

<DataTemplate x:Key="グループヘッダTemplate">
    <StackPanel Background="#FFCDF2CD">
        <TextBlock Text="{Binding Path=Items[0], Converter={StaticResource ランナー名取得Converter}}" Margin="3.5,0" FontWeight="Bold"/>
        <Rectangle Height="1" Fill="Blue"/>
    </StackPanel>
</DataTemplate>

あとは、ランナー名取得Converterを作るだけです。

[ValueConversion(typeof(CollectionViewGroup), typeof(string))]
public class ランナー名取得Converter : IValueConverter
{
    #region IValueConverter メンバ

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        dynamic d = value;

        return ((ランナー)d).ランナー名;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

ここで渡されるvalueの型はMS.Internal.Data.CollectionViewGroupInternalなので、((MS.Internal.Data.CollectionViewGroupInternal)value).Items[0]とすれば実際のランナー型のオブジェクトが得られるのですが、MS.Internal.Data.CollectionViewGroupInternalは、おそらく参照設定できないのではないかと思い(よく調べてませんが・・・)、dynamicで受けています。
以上で実行すると、以下のように表示されます。思った通りですね!

最後にソースを載せておきます。

<Window x:Class="WPFGroupingExamples.グループヘッダコンバーター使用View"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WPFGroupingExamples"
        Title="グループヘッダコンバーター使用View"  Height="393" Width="693">
    
    <Window.DataContext>
        <local:グループヘッダコンバーター使用ViewModel />
    </Window.DataContext>

    <Window.Resources>
        <local:ランナー名取得Converter x:Key="ランナー名取得Converter" />

        <DataTemplate x:Key="グループヘッダTemplate">
            <StackPanel Background="#FFCDF2CD">
                <TextBlock Text="{Binding Path=Items[0], Converter={StaticResource ランナー名取得Converter}}" Margin="3.5,0" FontWeight="Bold"/>
                <Rectangle Height="1" Fill="Blue"/>
            </StackPanel>
        </DataTemplate>

    </Window.Resources>

    <Grid>

        <ListView ItemsSource="{Binding UICollectionViewSource.View, Mode=OneWay}" Margin="0,0,0,37">

            <ListView.GroupStyle>
                <GroupStyle HeaderTemplate="{StaticResource グループヘッダTemplate}"/>
            </ListView.GroupStyle>

            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="大会ID" DisplayMemberBinding="{Binding 大会ID}" Width="Auto" />
                        <GridViewColumn Header="大会名" DisplayMemberBinding="{Binding 大会名}" Width="Auto" />
                        <GridViewColumn Header="ランナーID" DisplayMemberBinding="{Binding ランナーID}" Width="Auto" />
                        <GridViewColumn Header="ランナー名" DisplayMemberBinding="{Binding ランナー名}" Width="Auto" />
                        <GridViewColumn Header="タイム" DisplayMemberBinding="{Binding タイム}" Width="Auto" />
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>

        <Button Content="ソート・グループ化適用" Command="{Binding ソート・グループ化適応Command, Mode=OneWay}" Height="23" HorizontalAlignment="Left" Margin="100,329,0,0" Name="button1" VerticalAlignment="Top" Width="157" />

    </Grid>

</Window>
using System;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.ComponentModel;
using System.Windows.Input;
using WPFGroupingExamples.Helpers;

namespace WPFGroupingExamples
{
    class グループヘッダコンバーター使用ViewModel
    {
        #region コンストラクタ

        public グループヘッダコンバーター使用ViewModel()
        {
            ソート・グループ化適応Command = new RelayCommand(ソート・グループ設定);

            ObservableCollection<ランナー> uiobjects = new ObservableCollection<ランナー>();

            uiobjects.Add(new ランナー { 大会ID = 1, 大会名 = "サロマ100km", ランナーID = 1, ランナー名 = "関西太郎", タイム = new TimeSpan(11, 59, 35) });
            uiobjects.Add(new ランナー { 大会ID = 2, 大会名 = "隠岐ウルトラ100km", ランナーID = 1, ランナー名 = "関西太郎", タイム = new TimeSpan(12, 22, 31) });
            uiobjects.Add(new ランナー { 大会ID = 3, 大会名 = "四万十川ウルトラ100km", ランナーID = 1, ランナー名 = "関西太郎", タイム = new TimeSpan(12, 01, 28) });
            uiobjects.Add(new ランナー { 大会ID = 1, 大会名 = "サロマ100km", ランナーID = 2, ランナー名 = "関東洋子", タイム = new TimeSpan(11, 16, 22) });
            uiobjects.Add(new ランナー { 大会ID = 2, 大会名 = "隠岐ウルトラ100km", ランナーID = 2, ランナー名 = "関東洋子", タイム = new TimeSpan(11, 45, 42) });
            uiobjects.Add(new ランナー { 大会ID = 3, 大会名 = "四万十川ウルトラ100km", ランナーID = 2, ランナー名 = "関東洋子", タイム = new TimeSpan(11, 14, 17) });

            uiobjects.Add(new ランナー { 大会ID = 1, 大会名 = "サロマ100km", ランナーID = 3, ランナー名 = "関西太郎", タイム = new TimeSpan(10, 23, 11) });

            UICollectionViewSource = new CollectionViewSource();
            UICollectionViewSource.Source = uiobjects;
        }

        #endregion

        #region プロパティ

        /// <summary>
        /// UIに表示するCollectionViewSourceを取得する。
        /// </summary>
        CollectionViewSource _UICollectionViewSource;
        public CollectionViewSource UICollectionViewSource { get { return _UICollectionViewSource; } set { _UICollectionViewSource = value; } }

        #endregion

        #region コマンド

        /// <summary>
        /// ソートおよびグループ化を実行するコマンド
        /// </summary>
        public ICommand ソート・グループ化適応Command { get; private set; }

        #endregion

        #region コマンドハンドラ

        /// <summary>
        /// ソートおよびグループ化を実行する。
        /// </summary>
        private void ソート・グループ設定(object obj)
        {
            SortDescription sortDescription;
            PropertyGroupDescription groupDescription;

            //ソートの指定
            UICollectionViewSource.SortDescriptions.Clear();

            sortDescription = new SortDescription
            {
                PropertyName = "大会ID",
                Direction = ListSortDirection.Ascending
            };
            UICollectionViewSource.SortDescriptions.Add(sortDescription);

            sortDescription = new SortDescription
            {
                PropertyName = "ランナーID",
                Direction = ListSortDirection.Ascending
            };
            UICollectionViewSource.SortDescriptions.Add(sortDescription);

            //グループの指定
            UICollectionViewSource.GroupDescriptions.Clear();

            //groupDescription = new ランナーGroupDescription
            groupDescription = new PropertyGroupDescription
            {
                PropertyName = "ランナーID"
            };
            UICollectionViewSource.GroupDescriptions.Add(groupDescription);

            UICollectionViewSource.View.Refresh();
        }

        #endregion

        //#region GroupDescription

        //class ランナーGroupDescription : PropertyGroupDescription
        //{
        //    public override object GroupNameFromItem(object item, int level, System.Globalization.CultureInfo culture)
        //    {
        //        var uiobject = (ランナー)item;

        //        return uiobject.ランナー名;
        //    }
        //}

        //#endregion
    }

    #region コンバーター

    [ValueConversion(typeof(CollectionViewGroup), typeof(string))]
    public class ランナー名取得Converter : IValueConverter
    {
        #region IValueConverter メンバ

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            dynamic d = value;

            return ((ランナー)d).ランナー名;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

    #endregion

    #region ランナー クラス

    class ランナー
    {
        public int 大会ID { get; set; }
        public string 大会名 { get; set; }
        public int ランナーID { get; set; }
        public string ランナー名 { get; set; }
        public TimeSpan タイム { get; set; }
    }

    #endregion
}

CollectionViewSourceを使ったグルーピングあれこれ【その2 GroupNameFromItemオーバーライド時の問題】

CollectionViewSourceを使ったグルーピングあれこれ【その1】
http://d.hatena.ne.jp/trapemiya/?_ts=1344387721

の、続きになります。えっと、前回紹介したコードは基本的なことだったので全く説明していなかったのですが、念のために軽く説明しておきますね。
前回のコードでの肝は、

groupDescription = new ランナーGroupDescription
{
    PropertyName = "ランナーID"
};

の部分です。グルーピングする際には基本的にPropertyGroupDescriptionクラスのインスタンスを作成し、CollectionViewSourceのPropertyGroupDescriptionに追加します。つまり、以下のようになります。

groupDescription = new PropertyGroupDescription
{
    PropertyName = "ランナーID"
};

しかし、このようにするとグルーピングのヘッダにはPropertyNameで指定した値が表示されてしまうのです。つまり、以下のようになります。

グルーピングヘッダにランナーIDが表示されてもあまりうれしくないですよね。よって、PropertyGroupDescriptionのGroupNameFromItemをオーバーライドして、ランナー名を返しているわけです。

class ランナーGroupDescription : PropertyGroupDescription
{
    public override object GroupNameFromItem(object item, int level, System.Globalization.CultureInfo culture)
    {
         var uiobject = (ランナー)item;

         return uiobject.ランナー名;
    }
}

んがっ、これで、めでたしめでたしとはいかないのです。
GroupNameFromItemをオーバーライドすると、GroupNameFromItemで返された値でグルーピングされてしまいます。これはなかなか気づきにくいところで、はまるところだと思います。試しに、ランナーIDが1である関西太郎さんと同姓同名であるランナーIDが3の関西太郎さんを以下のように追加してみましょう。

uiobjects.Add(new ランナー { 大会ID = 1, 大会名 = "サロマ100km", ランナーID = 3, ランナー名 = "関西太郎", タイム = new TimeSpan(10, 23, 11) });

結果は以下のようになります。

ランナー名でグルーピングされますから、同姓同名が混ざってしまっています。これはまずいですね。では、どう対応すれば良いのでしょうか?次回はこれについて触れたいと思います。

CollectionViewSourceを使ったグルーピングあれこれ【その1】

CollectionViewSourceを使った応用や問題点、およびその解決策について何回かに渡って紹介します。
今回は【その1】として、簡単なグルーピングを行った例を掲載します。これを元にいろいろ発展させていく予定です。

<Window x:Class="WPFGroupingExamples.単純GroupingView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WPFGroupingExamples"
        Title="単純GroupingView" Height="393" Width="693">
    
    <Window.DataContext>
        <local:単純GroupingViewModel />
    </Window.DataContext>
    
    <Window.Resources>

        <DataTemplate x:Key="グループヘッダTemplate">
            <StackPanel Background="#FFCDF2CD">
                <TextBlock Text="{Binding Name}" FontWeight="Bold"/>
                <Rectangle Height="1" Fill="Blue"/>
            </StackPanel>
        </DataTemplate>

    </Window.Resources>
    
    <Grid>
        
        <ListView ItemsSource="{Binding UICollectionViewSource.View, Mode=OneWay}" Margin="0,0,0,37">

             <ListView.GroupStyle>
                <GroupStyle HeaderTemplate="{StaticResource グループヘッダTemplate}"/>
            </ListView.GroupStyle>

            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="大会ID" DisplayMemberBinding="{Binding 大会ID}" Width="Auto" />
                        <GridViewColumn Header="大会名" DisplayMemberBinding="{Binding 大会名}" Width="Auto" />
                        <GridViewColumn Header="ランナーID" DisplayMemberBinding="{Binding ランナーID}" Width="Auto" />
                        <GridViewColumn Header="ランナー名" DisplayMemberBinding="{Binding ランナー名}" Width="Auto" />
                        <GridViewColumn Header="タイム" DisplayMemberBinding="{Binding タイム}" Width="Auto" />
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
        
        <Button Content="ソート・グループ化適用" Command="{Binding ソート・グループ化適応Command, Mode=OneWay}"
               Height="23" HorizontalAlignment="Left" Margin="100,329,0,0" Name="button1" VerticalAlignment="Top" Width="157" />
    
    </Grid>
    
</Window>

以下に出てくるRelayCommandについては、以下を参考にして下さい。

Model-View-ViewModel デザイン パターンによる WPF アプリケーション
http://msdn.microsoft.com/ja-jp/magazine/dd419663.aspx
using System;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.ComponentModel;
using System.Windows.Input;
using WPFGroupingExamples.Helpers;

namespace WPFGroupingExamples
{
    class 単純GroupingViewModel
    {
        #region コンストラクタ

        public 単純GroupingViewModel()
        {
            ソート・グループ化適応Command = new RelayCommand(ソート・グループ設定);

            ObservableCollection<ランナー> uiobjects = new ObservableCollection<ランナー>();

            uiobjects.Add(new ランナー { 大会ID = 1, 大会名 = "サロマ100km", ランナーID = 1, ランナー名 = "関西太郎", タイム = new TimeSpan(11, 59, 35) });
            uiobjects.Add(new ランナー { 大会ID = 2, 大会名 = "隠岐ウルトラ100km", ランナーID = 1, ランナー名 = "関西太郎", タイム = new TimeSpan(12, 22, 31) });
            uiobjects.Add(new ランナー { 大会ID = 3, 大会名 = "四万十川ウルトラ100km", ランナーID = 1, ランナー名 = "関西太郎", タイム = new TimeSpan(12, 01, 28) });
            uiobjects.Add(new ランナー { 大会ID = 1, 大会名 = "サロマ100km", ランナーID = 2, ランナー名 = "関東洋子", タイム = new TimeSpan(11, 16, 22) });
            uiobjects.Add(new ランナー { 大会ID = 2, 大会名 = "隠岐ウルトラ100km", ランナーID = 2, ランナー名 = "関東洋子", タイム = new TimeSpan(11, 45, 42) });
            uiobjects.Add(new ランナー { 大会ID = 3, 大会名 = "四万十川ウルトラ100km", ランナーID = 2, ランナー名 = "関東洋子", タイム = new TimeSpan(11, 14, 17) });

            UICollectionViewSource = new CollectionViewSource();
            UICollectionViewSource.Source = uiobjects;
        }

        #endregion

        #region プロパティ

        /// <summary>
        /// UIに表示するCollectionViewSourceを取得する。
        /// </summary>
        CollectionViewSource _UICollectionViewSource;
        public CollectionViewSource UICollectionViewSource { get { return _UICollectionViewSource; } set { _UICollectionViewSource = value; } }

        #endregion

        #region コマンド

        /// <summary>
        /// ソートおよびグループ化を実行するコマンド
        /// </summary>
        public ICommand ソート・グループ化適応Command { get; private set; }

        #endregion

        #region コマンドハンドラ

        /// <summary>
        /// ソートおよびグループ化を実行する。
        /// </summary>
        private void ソート・グループ設定(object obj)
        {
            SortDescription sortDescription;
            PropertyGroupDescription groupDescription;

            //ソートの指定
            UICollectionViewSource.SortDescriptions.Clear();

            sortDescription = new SortDescription
            {
                PropertyName = "大会ID",
                Direction = ListSortDirection.Ascending
            };
            UICollectionViewSource.SortDescriptions.Add(sortDescription);

            sortDescription = new SortDescription
            {
                PropertyName = "ランナーID",
                Direction = ListSortDirection.Ascending
            };
            UICollectionViewSource.SortDescriptions.Add(sortDescription);

            //グループの指定
            UICollectionViewSource.GroupDescriptions.Clear();

            groupDescription = new ランナーGroupDescription
            {
                PropertyName = "ランナーID"
            };
            UICollectionViewSource.GroupDescriptions.Add(groupDescription);

            UICollectionViewSource.View.Refresh();
        }

        #endregion

        #region GroupDescription

        class ランナーGroupDescription : PropertyGroupDescription
        {
            public override object GroupNameFromItem(object item, int level, System.Globalization.CultureInfo culture)
            {
                var uiobject = (ランナー)item;

                return uiobject.ランナー名;
            }
        }

        #endregion
    }

    #region ランナー クラス

    class ランナー
    {
        public int 大会ID { get; set; }
        public string 大会名 { get; set; }
        public int ランナーID { get; set; }
        public string ランナー名 { get; set; }
        public TimeSpan タイム { get; set; }
    }

    #endregion

}

上記を実行すると以下の画面が表示されます。

「ソート・グループ化適用」ボタンを押すと、次のように表示されます。

Microsoft MVP C# 10月度再受賞しました。

ご報告が大変遅くなりましたが、今年もよろしくお願いいたします。今年で7年目です。7年の間にテクノロジーもずいぶん変わりましたが、好きでやっていることなのでこれからもいろんなテクノロジーで楽しませてもらえたらと思っています。
ところで話は変わりますが、trapemiyaってまともに読まれたことが無いので、読み方と由来をプロフィールに書いておきました。毎年、某Open Dayで読み方を間違えられている気がしますw
私がちゃんと書いていないのがいけなかったんですけどね。今度、某Open Dayに出る機会があれば、名札に本名に加えてふりがなも書いてもらうようにリクエストしてみます。手間かけてすみませぬ。

RadioButtonのチェックがコードからCheckできなくなるバグはWPF4でも未だ直っていないようだ。【その2】

RadioButtonのチェックがコードからCheckできなくなるバグはWPF4でも未だ直っていないようだ。
http://d.hatena.ne.jp/trapemiya/?_ts=1308293087

の続きです。この件に関して、twitterで@frozenlibさんからするどい突込みがありました。引用させていただきます。

@trapemiya ConvertBackが常にparameterを返しているのがマズいんじゃないでしょうか?valueがfalseの時はDependencyProperty.UnsetValueを返すようにすると、ちゃんと動いているように見えます。

問題のコードはこれ。

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return parameter;
        }

確かにfalseの時にparameterを返すのはおかしいですね。そこで、@frozenlibさんが言われる通りに修正。

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
          //  return parameter;

            if ((bool)value)
                return parameter;
            else
                return DependencyProperty.UnsetValue;
        }

すばらしい! 完璧に動いているようです。 というわけで、DependencyProperty.UnsetValueの存在すら気づいていなかった無知の私でした。
@frozenlibさん、ありがとうございました。

RadioButtonのチェックがコードからCheckできなくなるバグはWPF4でも未だ直っていないようだ。

このバグは以下のコードで再現される。

<Window x:Class="test2010wpf.RadioButtonTest"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:local="clr-namespace:test2010wpf"    
        Title="RadioButtonTest" Height="150" Width="502">

    <Window.Resources>
        <local:BoolToByteConverter x:Key="boolToByteConverter" />
    </Window.Resources>
    
    <Grid>
        <StackPanel Margin="0,0,0,51">
            
            <RadioButton Content="RadioButton1" Height="16" Name="radioButton1"
                        IsChecked="{Binding OptionType, Converter={StaticResource boolToByteConverter}, ConverterParameter=1}" />
        
            <RadioButton Content="RadioButton2" Height="16" Name="radioButton2"
                        IsChecked="{Binding OptionType, Converter={StaticResource boolToByteConverter}, ConverterParameter=2}"/>
            
            <RadioButton Content="RadioButton3" Height="16" Name="radioButton3"
                        IsChecked="{Binding OptionType, Converter={StaticResource boolToByteConverter}, ConverterParameter=3}" />
        
        </StackPanel>
        
        <Button Content="Select RadioButton1" Height="23" HorizontalAlignment="Left" Margin="0,76,0,0" Name="button1"
                VerticalAlignment="Top" Width="132" Click="button1_Click" />
        <Button Content="Select RadioButton2" Height="23" HorizontalAlignment="Left" Margin="147,76,0,0" Name="button2"
                VerticalAlignment="Top" Width="132" Click="button2_Click" />
        <Button Content="Select RadioButton3" Height="23" HorizontalAlignment="Left" Margin="294,76,0,0" Name="button3"
                VerticalAlignment="Top" Width="132" Click="button3_Click" />
    </Grid>
</Window>
using System;
using System.Windows;
using System.Windows.Data;
using System.Globalization;

namespace test2010wpf
{
    /// <summary>
    /// RadioButtonTest.xaml の相互作用ロジック
    /// </summary>
    public partial class RadioButtonTest : Window
    {
        public RadioButtonTest()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        // Dependency Property
        public static readonly DependencyProperty OptionTypeProperty =
                    DependencyProperty.Register("OptionType", typeof(byte), typeof(RadioButtonTest));

        // CLR Property Wrapper
        public byte OptionType
        {
            get { return (byte)GetValue(OptionTypeProperty); }
            set { SetValue(OptionTypeProperty, value); }
        }


        private void button1_Click(object sender, RoutedEventArgs e)
        {
            OptionType = 1;
        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            OptionType = 2;
        }

        private void button3_Click(object sender, RoutedEventArgs e)
        {
            OptionType = 3;
        }
    }

    public class BoolToByteConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var v = (byte)value;

            if (v == byte.Parse(parameter.ToString()))
                return true;
            else
                return false;
        }
        
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return parameter;
        }
    }
}

上記のコードを実行すると以下の画面が表示される。

この画面でRadioButtonにチェックするために適当に「Select RadioButton1」、「Select RadioButton2」、「Select RadioButton3」を押していくと、
やがてそれらのボタンを押してもRadioButtonが反応しなくなる。これについては以下によればバグであり、WPF 4で既に修正されているとのことであるが、
私が試した限り、WPF 4でも修正されていない。

RadioButton unchecked bindings issue still not resolved? http://social.msdn.microsoft.com/forums/en-US/wpf/thread/8eb8280a-19c4-4502-8260-f74633a9e2f2/

このバグの対処としては、上記のリンク先に書いてある通り、異なるGroupNameを付ければ良い。つまり、以下のようにすれば良い。

    <RadioButton Content="RadioButton1" Height="16" Name="radioButton1"
                GroupName="a"
                IsChecked="{Binding OptionType, Converter={StaticResource boolToByteConverter}, ConverterParameter=1}" />

     <RadioButton Content="RadioButton2" Height="16" Name="radioButton2"
                GroupName="b"
                IsChecked="{Binding OptionType, Converter={StaticResource boolToByteConverter}, ConverterParameter=2}"/>

     <RadioButton Content="RadioButton3" Height="16" Name="radioButton3"
                GroupName="c"
                IsChecked="{Binding OptionType, Converter={StaticResource boolToByteConverter}, ConverterParameter=3}" />

しかし、上記の対処方法は、StackPanelを使わずにGroupNameを同じにすることによって3つのRadioButtonをグルーピングしている場合には使用できない。この場合の対処方法はいろいろ試した結果見つけたのであるが、以下のようにどのRadioButtonも取りえない値を一度セットすれば良いようだ。原因も何故うまく動くのかもわからなので保障はしませんがw

private void button1_Click(object sender, RoutedEventArgs e)
{
    OptionType = 255;
    OptionType = 1;
}

private void button2_Click(object sender, RoutedEventArgs e)
{
    OptionType = 255;
    OptionType = 2;
}

private void button3_Click(object sender, RoutedEventArgs e)
{
    OptionType = 255;
    OptionType = 3;
}