我有一個數據對象中的選項列表,我希望使單選按鈕列表等同於允許用戶選擇其中的一個,並且只能選擇其中一個。功能類似於數據綁定組合框,但採用單選按鈕格式。WPF中的數據綁定單選按鈕列表
傻了我,我以爲這是內置的,但沒有。你怎麼做呢?
我有一個數據對象中的選項列表,我希望使單選按鈕列表等同於允許用戶選擇其中的一個,並且只能選擇其中一個。功能類似於數據綁定組合框,但採用單選按鈕格式。WPF中的數據綁定單選按鈕列表
傻了我,我以爲這是內置的,但沒有。你怎麼做呢?
基本上,在回顧了谷歌搜索結果之後,我開始從an MSDN discussion thread where Dr. WPF provided an answer獲取信息,該信息討論了對ListBox進行樣式設計以使其看起來正確。但是,當列表框被禁用時,背景是一種令人討厭的顏色,在我讀到the MSDN example of the ListBox ControlTemplate之前,我無法擺脫我的生活,直到我看到踢着我的背景屁股的祕密邊框元素。
所以,在這裏最後的答案是這樣的風格:
<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 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,我認爲它使平凡的出奇的困難和瘋狂難以瑣碎的更多。請享用。 -Scott
我已通過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
上使用綁定。
最後一句是痛苦的真實:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/88a22766-5e6f-4a16-98a6 -1ab39877dd09/ – jan 2011-08-12 08:49:04
綁定列表框與有一個屬性名稱的對象列表的ListBox的ItemsSource時(這可以改變)
<ListBox Name="RadioButtonList">
<ListBox.ItemTemplate >
<DataTemplate >
<RadioButton GroupName="radioList" Tag="{Binding}" Content="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
重要組名=「單選列表」
我被騙了:
我的解決辦法是綁定列表框programaticly,因爲這是所有似乎爲我工作:
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"];
}
希望幫助別人。
我從Jon Benson's blog entry中獲得靈感,但修改了他的解決方案以使用具有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");
}
}
}
}
對不起,我想將這個迴應斯科特·O公司的崗位作爲他的訊息發表評論,但我還沒有信譽做到這一點。我真的很喜歡他的答案,因爲它是一個只風格的解決方案,因此不需要添加任何代碼隱藏或創建一個自定義的控制等
不過,我也有一個問題,當我後來去試試使用ListBoxItems中的控件。當我使用這種風格,我無法對焦任何包含的控件由於這一行:
<RadioButton Focusable="False" IsHitTestVisible="False" IsChecked="{TemplateBinding IsSelected}">
的單選按鈕,需要關閉調焦和IsHitTestVisible的結合器isChecked正常工作。爲了解決這個問題,我將IsChecked從TemplateBinding更改爲常規綁定,這使我可以將其設置爲雙向綁定。拆除違規設置給了我這一行:
<RadioButton IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected, Mode=TwoWay}">
現在可以讓我集中包含在ListBoxItems任何控件的預期。
希望這會有所幫助。
超簡單,MVVM友好,利用DataTemplates的類型。 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將自動使用它。 (在ListBox資源中定義DataTemplate以限制DataTemplate的應用範圍)。
如果需要,還可以使用DataTemplate中的組名稱來設置組。
這比改變控件模板簡單得多,但它確實意味着你在選定的項目上會出現一條藍線。 (再一次,沒有什麼樣式不能修復)。
當你知道如何WPF很簡單。
+1對WPF的評論使得微不足道的問題變得困難,反之亦然。我認爲我們在這裏遇到了一些嚴重的早期採用者問題。來自另一場討論的這篇文章有一個很好的解決方案來綁定到枚舉。它與這個解決方案很好地發揮: –
2009-07-07 18:47:32
我經常發現它更容易使用當我需要一個重複的模板時,一個ItemsControl比ListBox。 – xr280xr 2013-03-21 18:34:06
@PaulPrewett nope - 那些問題仍然存在;-) – 2015-04-19 19:02:32