65.9K
CodeProject 正在变化。 阅读更多。
Home

带单选按钮的 RadioListBox(WPF 版本)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (7投票s)

2009年9月6日

CPOL

2分钟阅读

viewsIcon

134092

downloadIcon

4155

如何实现一个带有单选按钮而不是标准选择高亮的模板化列表框。

RadioListBoxWPF_Source

RadioListBoxWPF_Source

引言

这是我之前文章的 WPF 版本:CRadioListBox:一个带单选按钮的列表框 (MFC 版本)RadioListBox:一个带单选按钮的列表框 (WinForms 版本)。正如那些文章所解释的,RadioListBox 具有与常规 ListBox 相同的功能,但外观和感觉不同。无论如何,RadioListBox 控件提供了一些优势

  • 使用单选按钮可以更清楚地表明选项是互斥的。
  • 它是单选按钮组的良好替代方案,因为您只需要维护一个控件,从而减少内存使用。
  • 它继承了一些有用的功能,如滚动、排序、数据绑定和多列。
  • 动态更改选项将更容易,如演示应用程序所示。
  • 管理选择事件也将更容易,同样在演示应用程序中显示。

Using the Code

要在您的项目中实现 RadioListBox,您只需要执行几个步骤

  • RadioListBox.xamlRadioListBox.xaml.cs 包含到您的项目中。
  • 将一个 RadioListBox 对象放入您的 XAML 窗体中,或手动操作。System.Windows.Controls.Custom 命名空间的别名将很有用。
  • 更改控件的标准属性,就像对于 ListBox 一样。
  • 使用 IsTransparent 属性来模仿一组 RadioButton

就这样!现在您可以将单选按钮集合用作常规 ListBox。您可以使用 Items.Add() 方法、ItemsSource 属性,或直接在 XAML 中添加项目。最后,使用 SelectedIndex 属性查询用户选择。不再需要逐个评估。

RadioListBox 内部

RadioListBox 类派生自 WPF 的 ListBox 类,带有一个自定义的 ControlTemplate,用于删除所有不需要的 ListBox 功能,如所选项目的背景。为了与单选按钮模型保持一致,已禁用多选。 摘要 XAML 实现如下所示

<ListBox x:Class="System.Windows.Controls.Custom.RadioListBox"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:s="clr-namespace:System;assembly=mscorlib" >
    <ListBox.Resources>
        <Style x:Key="{x:Type ListBoxItem}" TargetType="ListBoxItem">
            <Setter Property="SnapsToDevicePixels" Value="true"/>
            <Setter Property="OverridesDefaultStyle" Value="true"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <RadioButton x:Name="radio" Click="ItemRadioClick">
                            <RadioButton.Content>
                                <ContentPresenter ... />
                            </RadioButton.Content>
                        </RadioButton>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.Resources>
    <ListBox.Template>
        <ControlTemplate>
            <Border BorderThickness="0" 
                  Padding="1,1,1,1" 
                  Background="Transparent" 
                  Name="theBorder" 
                  SnapsToDevicePixels="True">
                <ScrollViewer Padding="{TemplateBinding Control.Padding}" 
                              Focusable="False">
                  <ItemsPresenter 
                      SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
                </ScrollViewer>
            </Border>
            <ControlTemplate.Triggers ... />
        </ControlTemplate>
    </ListBox.Template>
</ListBox>

由于 ControlTemplate 内部的单选按钮不会更改选择,因此必须在 ItemRadioClick 事件内部手动完成。在这里,ItemContainerGenerator 属性通过在 ListBox 的项目内容和 ListBoxItem 呈现对象之间进行转换而扮演重要角色。以下是简要的 C# 源代码

namespace System.Windows.Controls.Custom
{
    public partial class RadioListBox : ListBox
    {
        public RadioListBox() { ... }

        public new SelectionMode SelectionMode { ... }

        public bool IsTransparent { ... }

        private void ItemRadioClick(object sender, RoutedEventArgs e)
        {
            ListBoxItem sel = (e.Source as RadioButton).TemplatedParent as ListBoxItem;
            int newIndex = this.ItemContainerGenerator.IndexFromContainer(sel); ;
            this.SelectedIndex = newIndex;
        }

        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            base.OnSelectionChanged(e);

            CheckRadioButtons(e.RemovedItems, false);
            CheckRadioButtons(e.AddedItems, true);
        }

        private void CheckRadioButtons(System.Collections.IList radioButtons, bool isChecked) 
        {
            foreach (object item in radioButtons)
            {
                ListBoxItem lbi = 
                  this.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem;

                if (lbi != null)
                {
                    RadioButton radio = lbi.Template.FindName("radio", lbi) as RadioButton;
                    if (radio != null)
                        radio.IsChecked = isChecked;
                }
            }
        }
    }
}

历史

  • 2009年9月6日:第一个版本。
© . All rights reserved.