7487


WPFで、同じ`GroupName`を持つ`RadioButton`のリストを構成するカスタムコントロールを作ろうとしています。  
このカスタムコントロールは、次の`DependencyProperty`を持ちます。

`ItemsSource`：`enum`のコレクション（`enum`なら何でもOK）  
`SelectedValue`：選択されている`RadioButton`に対応する`enum`の値

`enum`の値から`RadioButton`の表示テキスト(`Content`)への変換は、`IValueConverter`を実装して適当に処理しています。  
`ItemsSource`から`RadioButton`を生成する処理には、`ItemsControl`を利用しました。

ここまでは問題なかったのですが、`SelectedValue`プロパティと、対応する`RadioButton`の`IsChecked`を結びつける方法が分かりません。  
`ItemsControl`を使わず、XAMLで静的に作られたものであれば、各`RadioButton`の`CoverterParameter`に対応する`enum`の値を記述し、`Converter`に渡すだけで良いことは分かるのですが、動的にラジオボタンを作ろうとして詰まってしまいました。  
下記のコードの、`ConvertParameter={Binding}`の部分が問題です。

[code]

    <ItemsControl Name="itemsControl" ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
    
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <RadioButton IsChecked="{
                                 Binding SeletedValue,
                                 Converter={StaticResource RadioButtonCheckedConverter},
                                 ConverterParameter={Binding},
                                 RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}
                             }"
                             GroupName="{Binding GroupName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
                             Content="{Binding Converter={StaticResource RadioButtonTextConverter}}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    
    </ItemsControl>
    
[/code]

このままでは

[code]

    型 'Binding' の 'ConverterParameter' プロパティで 'Binding' を設定することはできません。
    'Binding' は、DependencyObject の DependencyProperty でのみ設定できます。
    
[/code]

というエラーが出てしまい、動作しません。  
何とかして、`RadioButtonCheckedConverter`に各`RadioButton`にバインドされている`enum`の値を`ConverterParameter`として渡したいのですが・・・  
どなたか何かご存知でしたら、教えてください。


CC BY-SA 3.0
2015-03-05T01:14:41.333
7457
2015-03-05T11:54:24.960
8078
question
wpf xaml
RadioButtonのカスタムコントロールにenumのリストをバインドしたい


ListBoxに変更して、RadioButtonのIsCheckedとListBoxItemのIsSelectedをバインドすればよいのではないでしょうか。  
以下はListBoxの背景色を消すためにいくつかスタイルを操作していますが、必要なのはRadioButtoのプロパティに行っているバインドのみです。

[code]

    <Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"       
        Title="MainWindow" Height="200" Width="200">
    <Window.Resources>
        <!-- Enumの要素一覧に変換するコンバーター -->
        <local:RadioTool x:Key="enumConverter"/>        
        <DataTemplate x:Key="radioTemplate">
            <!-- ListBoxの選択色を消すためにRadioButtonの外にGrid配置してます-->
            <Grid Background="{Binding Path=Background,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ItemsControl}}}">
                <RadioButton IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}}}"
                             GroupName="{Binding Path=(local:RadioTool.GroupName),RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ItemsControl}}}"
                             Content="{Binding}" />
            </Grid>
        </DataTemplate>
        <Style TargetType="{x:Type ListBoxItem}" x:Key="listBoxItemStyle">
            <!-- ListBoxの選択色を消すためにボーダーなどを消す -->
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="Padding" Value="0" />
        </Style>
        <Style TargetType="{x:Type ListBox}" x:Key="listBoxStyle">
            <Setter Property="ItemTemplate" Value="{StaticResource radioTemplate}" />
            <Setter Property="ItemContainerStyle" Value="{StaticResource listBoxItemStyle}" />
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
            <Setter Property="VerticalContentAlignment" Value="Stretch" />
        </Style>
    </Window.Resources>
    <StackPanel>
        <ListBox Style="{StaticResource listBoxStyle}"
                 ItemsSource="{Binding Source={x:Type Visibility},Converter={StaticResource enumConverter}}"                                  
                 local:RadioTool.GroupName="A" /><!-- 添付プロパティでRadioButtonのGroupNameを指定-->
        <ListBox Style="{StaticResource listBoxStyle}"
                 ItemsSource="{Binding Source={x:Type  HorizontalAlignment},Converter={StaticResource enumConverter}}"                 
                 local:RadioTool.GroupName="A"/>
    </StackPanel>
    
[/code]

##

[code]

    class RadioTool : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return Enum.GetValues((Type)value);
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    
        public static string GetGroupName(DependencyObject obj) { return (string)obj.GetValue(GroupNameProperty); }
    
        public static void SetGroupName(DependencyObject obj, string value) { obj.SetValue(GroupNameProperty, value); }
    
        public static readonly DependencyProperty GroupNameProperty
            = DependencyProperty.RegisterAttached("GroupName", typeof(string), typeof(RadioTool), new UIPropertyMetadata(default(string)));
    }  
    
[/code]

以上は、Enumが重複しない単一の値のみで定義されている場合のみ有効です。


CC BY-SA 3.0
2015-03-05T10:16:41.163
7482
2015-03-05T10:16:41.163
4926
7457
answer


添付プロパティを使用して`RadioButton`の`Checked`イベントを拾ってやる必要があると思います。  

[code]

    public class Radio
    {
        public static object GetSelectedValue(RadioButton obj)
        {
            return (object)obj.GetValue(SelectedValueProperty);
        }
    
        public static void SetSelectedValue(RadioButton obj, object value)
        {
            obj.SetValue(SelectedValueProperty, value);
        }
    
        public static readonly DependencyProperty SelectedValueProperty =
           DependencyProperty.RegisterAttached("SelectedValue", typeof(object), typeof(Radio), new FrameworkPropertyMetadata(null, OnSelectedValueChanged) { BindsTwoWayByDefault = true });
    
        private static void OnSelectedValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var rb = (RadioButton)d;
    
            rb.Checked -= rb_Checked;
            rb.Checked += rb_Checked;
            rb_Checked(rb, null);
        }
    
        static void rb_Checked(object sender, RoutedEventArgs e)
        {
            var rb = (RadioButton)sender;
            if (rb.IsChecked == true)
            {
                SetSelectedValue(rb, rb.DataContext);
            }
        }
    }
    
[/code]

使用例  

[code]

    <RadioButton local:Radio.SelectedValue="{Binding SelectedValue, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}" />
    
[/code]


CC BY-SA 3.0
2015-03-05T11:35:37.993
7487
2015-03-05T11:35:37.993
5750
7457
answer


`ConverterParameter`の値にはBindingできないので、他のアプローチを試すべきです。

やりたい事の本質としては「2つのBinding先の値と1つのUI要素を結び付けたい」という事でしょうから、`MultiBinding`と`IMultiValueConverter`を使えば実現できそうです。

[code]

    <RadioButton Content="{Binding Converter={StaticResource RadioButtonTextConverter}}" GroupName="{Binding GroupName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
        <RadioButton.IsChecked>
            <MultiBinding Converter="{StaticResource RadioButtonCheckedConverter}">
                <Binding Path="SelectedValue" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}" />
                <Binding Path="" />
            </MultiBinding>
        </RadioButton.IsChecked>
    </RadioButton>
    
[/code]

上記コードはあくまで一例(動作検証していない)ですが、こういった形で実現が可能かと思います。


CC BY-SA 3.0
2015-03-05T11:54:24.960
7492
2015-03-05T11:54:24.960
6119
7457
answer