代码之家  ›  专栏  ›  技术社区  ›  magol Nathaniel Roark

用XAML在列表框中添加一个额外的行

  •  0
  • magol Nathaniel Roark  · 技术社区  · 15 年前

    我有一个单选按钮在水平线上的列表框。单选按钮的数量是可选的。每个单选按钮的文本取自模型列表。选择哪个单选按钮将由属性SelectedOption决定。如果选择“无”,则应将其设置为-1。 问题是,除了模型提供的选项外,我还希望有一个选项“不知道”,将SelectedOption设置为-1。如何为我的列表框编写XAML来获得这个选项?

    型号:

    • IEnumerable<String> Descriptions -除了“不知道”之外,可用选项的描述性文本
    • Int SelectedOption -所选描述的索引-1如果选择“不知道”

    例子:

    ---------------------------------------------------------
    | () Option1 () Option2 () Option3        () Don’t know |
    ---------------------------------------------------------
    

    ()
    () Don’t know 有其他背景色吗

    1 回复  |  直到 15 年前
        1
  •  3
  •   Charlie    15 年前

    这是一个有趣的项目,需要不时进行一些黑客攻击。但我主要是通过多绑定和几个值转换器来管理它的。这个例子涵盖了您请求的每个特性,并且已经封装到一个单独的 Window 为了便于演示。首先,让我们从窗口的XAML开始,这里发生了很多神奇的事情:

    <Window x:Class="TestWpfApplication.BoundRadioButtonListBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:local="clr-namespace:TestWpfApplication"
    Title="BoundRadioButtonListBox" Height="200" Width="500"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Window.Resources>
        <local:ItemContainerToIndexConverter x:Key="ItemContainerToIndexConverter"/>
        <local:IndexMatchToBoolConverter x:Key="IndexMatchToBoolConverter"/>
    </Window.Resources>
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
    
        <ListBox ItemsSource="{Binding Models}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <ItemsControl x:Name="DescriptionList" ItemsSource="{Binding Descriptions}">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <RadioButton Content="{Binding}" Margin="5"
                                                 Command="{Binding RelativeSource={RelativeSource FindAncestor,
                                                 AncestorType={x:Type ItemsControl}}, Path=DataContext.CheckCommand}"
                                                 CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Tag}"
                                                 GroupName="{Binding RelativeSource={RelativeSource FindAncestor,
                                                 AncestorType={x:Type ItemsControl}}, Path=DataContext.GroupName}">
                                        <RadioButton.Tag>
                                            <MultiBinding Converter="{StaticResource ItemContainerToIndexConverter}">
                                                <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}"
                                                         Mode="OneWay"/>
                                                <Binding RelativeSource="{RelativeSource Self}" 
                                                         Path="DataContext"/>
                                            </MultiBinding>
                                        </RadioButton.Tag>
                                        <RadioButton.IsChecked>
                                            <MultiBinding Converter="{StaticResource IndexMatchToBoolConverter}">
                                                <Binding RelativeSource="{RelativeSource Self}" 
                                                         Path="Tag"/>
                                                <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}"
                                                         Path="DataContext.SelectedOption"/>
                                            </MultiBinding>
                                        </RadioButton.IsChecked>
                                    </RadioButton>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <StackPanel Orientation="Horizontal"/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                        </ItemsControl>
                        <Border Background="LightGray" Margin="15,5">
                            <RadioButton Content="Don't Know"
                                         Command="{Binding CheckCommand}"
                                         GroupName="{Binding GroupName}">
                                <RadioButton.CommandParameter>
                                    <sys:Int32>-1</sys:Int32>
                                </RadioButton.CommandParameter>
                            </RadioButton>
                        </Border>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    
        <StackPanel Grid.Row="1">
            <Label>The selected index for each line is shown here:</Label>
            <ItemsControl ItemsSource="{Binding Models}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Label Content="{Binding SelectedOption}"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </Grid>
    

    ListBox 一定是顶级车型。每种型号的 ItemTemplate 创建另一个嵌入的 ItemsControl ,用于显示项目描述。这就是我们如何支持动态数量的描述(这适用于任何数量的描述)。

    接下来,让我们查看此窗口的代码:

    /// <summary>
    /// Interaction logic for BoundRadioButtonListBox.xaml
    /// </summary>
    public partial class BoundRadioButtonListBox : Window
    {
        public ObservableCollection<LineModel> Models
        {
            get;
            private set;
        }
    
        public BoundRadioButtonListBox()
        {
            Models = new ObservableCollection<LineModel>();
    
            List<string> descriptions = new List<string>()
            {
                "Option 1", "Option 2", "Option 3"
            };
    
            LineModel model = new LineModel(descriptions, 2);
            Models.Add(model);
    
            descriptions = new List<string>()
            {
                "Option A", "Option B", "Option C", "Option D"
            };
    
            model = new LineModel(descriptions, 1);
            Models.Add(model);
    
            InitializeComponent();
        }
    }
    
    public class LineModel : DependencyObject
    {
        public IEnumerable<String> Descriptions
        {
            get;
            private set;
        }
    
        public static readonly DependencyProperty SelectedOptionProperty =
            DependencyProperty.Register("SelectedOption", typeof(int), typeof(LineModel));
    
        public int SelectedOption
        {
            get { return (int)GetValue(SelectedOptionProperty); }
            set { SetValue(SelectedOptionProperty, value); }
        }
    
        public ICommand CheckCommand
        {
            get;
            private set;
        }
    
        public string GroupName
        {
            get;
            private set;
        }
    
        private static int Index = 1;
    
        public LineModel(IEnumerable<String> descriptions, int selected)
        {
            GroupName = String.Format("Group{0}", Index++);
            Descriptions = descriptions;
            SelectedOption = selected;
            CheckCommand = new RelayCommand((index) => SelectedOption = ((int)index));
        }
    }
    

    所有这些都应该非常清楚。这个 LineModel 类表示您在问题中描述的模型。因此,它的特点是字符串描述的集合以及 SelectedOption 已经成为 DependencyProperty

    接下来是两个值转换器的代码:

    public class ItemContainerToIndexConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values.Length == 2 &&
                values[0] is ItemsControl &&
                values[1] is string)
            {
                ItemsControl control = values[0] as ItemsControl;
                ContentPresenter item = control.ItemContainerGenerator.ContainerFromItem(values[1]) as ContentPresenter;
                return control.ItemContainerGenerator.IndexFromContainer(item);
            }
            return -1;
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
    
    public class IndexMatchToBoolConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values.Length == 2 && 
                values[0] is int && 
                values[1] is int)
            {
                return (int)values[0] == (int)values[1];
            }
            return false;
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
    

    ItemContainerGenerator

    现在,完成的结果,100%数据绑定:

    alt text http://img210.imageshack.us/img210/2156/boundradiobuttons.png

    选定选项 正在更新模型上的属性。

    推荐文章