データ オブジェクトにオプションのリストがあり、ラジオ ボタン リストに相当するものを作成して、ユーザーがそれらのオプションを 1 つだけ選択できるようにしたいと考えています。データバインドされたコンボ ボックスに似た機能ですが、ラジオ ボタン形式です。
ばかげて、これは組み込まれていると思っていましたが、違います。どのようにしますか?
データ オブジェクトにオプションのリストがあり、ラジオ ボタン リストに相当するものを作成して、ユーザーがそれらのオプションを 1 つだけ選択できるようにしたいと考えています。データバインドされたコンボ ボックスに似た機能ですが、ラジオ ボタン形式です。
ばかげて、これは組み込まれていると思っていましたが、違います。どのようにしますか?
基本的に、Google の結果を確認した後、WPF 博士が回答を提供した MSDN ディスカッション スレッドからの情報から始めました。ただし、リストボックスが無効になっている場合、背景は煩わしい色であり、私の背景を蹴っていた秘密の Border 要素を示すListBox ControlTemplate の MSDN の例を読むまで、一生取り除くことができませんでした。お尻。
したがって、ここでの最終的な答えは次のスタイルでした。
<Style x:Key="RadioButtonList" TargetType="{x:Type ListBox}">
<!-- ControlTemplate taken from MSDN http://msdn.microsoft.com/en-us/library/ms754242.aspx -->
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="MinWidth" Value="120"/>
<Setter Property="MinHeight" Value="95"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<Border Name="Border" Background="Transparent"
BorderBrush="Transparent"
BorderThickness="0"
CornerRadius="2">
<ScrollViewer Margin="0" Focusable="false">
<StackPanel Margin="2" IsItemsHost="True" />
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Background"
Value="Transparent" />
<Setter TargetName="Border" Property="BorderBrush"
Value="Transparent" />
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Margin" Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border Name="theBorder" Background="Transparent">
<RadioButton Focusable="False" IsHitTestVisible="False"
IsChecked="{TemplateBinding IsSelected}">
<ContentPresenter />
</RadioButton>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
ListBox と Items の ControlTemplate とスタイルを提供します。そして、それは次のように使用されます:
<ListBox Grid.Column="1" Grid.Row="0" x:Name="TurnChargeBasedOnSelector" Background="Transparent"
IsEnabled="{Binding Path=IsEditing}"
Style="{StaticResource RadioButtonList}"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MainForm}}, Path=DataContext.RampTurnsBasedOnList}"
DisplayMemberPath="Description" SelectedValuePath="RampTurnsBasedOnID"
SelectedValue="{Binding Path=RampTurnsBasedOnID, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}"/>
WPF に時間を費やすほど、些細なことはめちゃくちゃ難しくなり、めちゃくちゃ難しいことは些細なことになると思います。楽しみ。-スコット
プロパティ Name を持つオブジェクトのリストを使用して、リストボックスを ListBox の ItemsSource にバインドします (これは変更される可能性があります)。
<ListBox Name="RadioButtonList">
<ListBox.ItemTemplate >
<DataTemplate >
<RadioButton GroupName="radioList" Tag="{Binding}" Content="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
重要なGroupName="radioList"
型に DataTemplates を活用した、非常にシンプルで MVVM フレンドリー。XAML:
<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="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Option}">
<RadioButton Focusable="False"
IsHitTestVisible="False"
Content="{Binding Display}"
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}">
</RadioButton>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Options}" SelectedItem="{Binding SelectedOption}"/>
</Grid>
ビュー モデルなど:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new Vm();
}
}
public class Vm
{
public Option[] Options { get { return new Option[] {
new Option() { Display = "A" },
new Option() { Display = "B" },
new Option() { Display = "C" } }; } }
public Option SelectedOption { get; set; }
}
public class Option
{
public string Display { get; set; }
}
オプションを特定のタイプにラップする場合(またはすでにラップしている可能性があります)。そのタイプの DataTemplate を設定するだけで、WPF は自動的にそれを使用します。(DataTemplate が適用される範囲を制限するには、ListBox リソースで DataTemplate を定義します)。
また、必要に応じて、DataTemplate でグループ名を使用してグループを設定します。
これは、コントロール テンプレートを変更するよりもはるかに簡単ですが、選択した項目に青い線が表示されることを意味します。(繰り返しますが、少しのスタイリングで修正できないものはありません)。
方法がわかれば、WPF は簡単です。
ValueConverter
を に変換するを介してこれを行いましenum
たbool
。ラジオ ボタンが表す列挙値を として渡すことによりConverterParameter
、コンバーターは、このラジオ ボタンをオンにするかどうかを返します。
<Window.Resources>
<Converters:EnumConverter x:Key="EnumConverter" />
</Window.Resources>
<RadioButton IsChecked="{Binding Path=MyEnum, Mode=TwoWay,
Converter={StaticResource EnumConverter},
ConverterParameter=Enum1}"}
Content="Enum 1" />
<RadioButton IsChecked="{Binding Path=MyEnum, Mode=TwoWay,
Converter={StaticResource EnumConverter},
ConverterParameter=Enum2}"}
Content="Enum 2" />
EnumConverter
は次のように定義されます。
public class EnumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType.IsAssignableFrom(typeof(Boolean)) && targetType.IsAssignableFrom(typeof(String)))
throw new ArgumentException("EnumConverter can only convert to boolean or string.");
if (targetType == typeof(String))
return value.ToString();
return String.Compare(value.ToString(), (String)parameter, StringComparison.InvariantCultureIgnoreCase) == 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType.IsAssignableFrom(typeof(Boolean)) && targetType.IsAssignableFrom(typeof(String)))
throw new ArgumentException("EnumConverter can only convert back value from a string or a boolean.");
if (!targetType.IsEnum)
throw new ArgumentException("EnumConverter can only convert value to an Enum Type.");
if (value.GetType() == typeof(String))
{
return Enum.Parse(targetType, (String)value, true);
}
//We have a boolean, as for binding to a checkbox. we use parameter
if ((Boolean)value)
return Enum.Parse(targetType, (String)parameter, true);
return null;
}
}
ラジオボタンを生成するために列挙型のリストにデータバインドしないことに注意してください。手動で行いました。バインディングを介してラジオ ボタンのリストを埋めたい場合は、バインディングをで使用できないため、バインディングを現在の値とラジオの列挙値の両方にバインドするIsChecked
に変更する必要があると思います。MultiBinding
ConverterParameter
申し訳ありませんが、Scott O の投稿に対するこの応答を彼の投稿へのコメントとして掲載したいと思いますが、私はまだそれを行う評判がありません。スタイルのみのソリューションであり、コードビハインドの追加やカスタムコントロールの作成などを必要としなかったため、私は彼の答えが本当に気に入りました.
ただし、ListBoxItems 内でコントロールを使用しようとしたときに、1 つの問題が発生しました。このスタイルを使用すると、次の行が原因で、含まれているコントロールのいずれにもフォーカスできません。
<RadioButton Focusable="False"
IsHitTestVisible="False"
IsChecked="{TemplateBinding IsSelected}">
IsChecked バインディングが正しく機能するためには、ラジオ ボタンで Focusable と IsHitTestVisible をオフにする必要があります。これを回避するために、IsChecked を TemplateBinding から通常のバインドに変更しました。これにより、双方向のバインドが可能になりました。問題のある設定を削除すると、次の行が表示されました。
<RadioButton IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected, Mode=TwoWay}">
これにより、期待どおりに ListBoxItems に含まれるすべてのコントロールにフォーカスできるようになりました。
お役に立てれば。
Jon Bensonのブログエントリからインスピレーションを得ましたが、description属性を持つ列挙を使用するように彼のソリューションを変更しました。したがって、ソリューションの重要な部分は次のようになりました。
説明付きの列挙子
public enum AgeRange {
[Description("0 - 18 years")]
Youth,
[Description("18 - 65 years")]
Adult,
[Description("65+ years")]
Senior,
}
説明を読み取り、バインドのためにキーと値のペアを返すためのコード。
public static class EnumHelper
{
public static string ToDescriptionString(this Enum val)
{
var attribute =
(DescriptionAttribute)
val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false).
SingleOrDefault();
return attribute == default(DescriptionAttribute) ? val.ToString() : attribute.Description;
}
public static List<KeyValuePair<string,string>> GetEnumValueDescriptionPairs(Type enumType)
{
return Enum.GetValues(enumType)
.Cast<Enum>()
.Select(e => new KeyValuePair<string, string>(e.ToString(), e.ToDescriptionString()))
.ToList();
}
}
XAMLのオブジェクトデータプロバイダー
<ObjectDataProvider
ObjectType="{x:Type local:EnumHelper}"
MethodName="GetEnumValueDescriptionPairs"
x:Key="AgeRanges">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:AgeRange" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
XAMLのリストボックス
<ListBox
ItemsSource="{Binding Source={StaticResource AgeRanges}}"
SelectedValue="{Binding SelectedAgeRange}"
SelectedValuePath="Key">
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}"
Content="{Binding Value}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
バインドしているプロパティ(ビューモデルなど)
public class YourViewModel : INotifyPropertyChanged
{
private AgeRange _selectedAgeRange;
public AgeRange SelectedAgeRange
{
get { return _selectedAgeRange; }
set
{
if (value != _selectedAgeRange)
{
_selectedAgeRange = value;
OnPropertyChanged("SelectedAgeRange");
}
}
}
}
私はだましました:
私の解決策は、リスト ボックスをプログラムでバインドすることでした。
if (mUdData.Telephony.PhoneLst != null)
{
lbPhone.ItemsSource = mUdData.Telephony.PhoneLst;
lbPhone.SelectedValuePath = "ID";
lbPhone.SelectedValue = mUdData.Telephony.PrimaryFaxID;
}
XAML は次のようになります。
<ListBox.ItemTemplate >
<DataTemplate >
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<RadioButton
IsChecked="{Binding Path=PrimaryPhoneID}"
GroupName="Phone"
x:Name="rbPhone"
Content="{Binding Path=PrimaryPhoneID}"
Checked="rbPhone_Checked"/>
<CheckBox Grid.Column="2" IsEnabled="False" IsChecked="{Binding Path=Active}" Content="{Binding Path=Number}" ></CheckBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
そして、私のイベントでは、ラジオボタンが選択されたときにその値を読み取ると、次のようになります。
private void rbPhone_Checked(object sender, RoutedEventArgs e)
{
DataRowView dvFromControl = null;
dvFromControl = (DataRowView)((RadioButton)sender).DataContext;
BindData.Telephony.PrimaryPhoneID = (int)dvFromControl["ID"];
}
それが誰かを助けることを願っています。