在WPF中,是否需要基于ListBox创建自定义RadioButtonList?

10

我继承了一个使用RadioButtonList的项目,它从ListBox继承。它已经从Web上删除了(目前找不到链接),包含RadioButtonList.cs(其中包含六个依赖属性)和RadioButtonList.xaml(仅为样式和控件模板)。

这个控件在100多个地方使用。它会带来问题,因为它不是完整的和专业的控件。会出现问题,例如焦点问题、键盘导航等。(请参阅评论。)

在过去几年中,经过多次研究,似乎这个控件实际上并不必要。需要做的只是在一组单选按钮上设置GroupName属性。而且,使用RadioButtonList控件的唯一原因是通过继承的ListBox帮助数据绑定选项列表。

1)这个控件真的必要吗?有更好的方法吗?

  • 如果您只需要一个单选按钮列表,则不需要此控件。
  • 如果您的应用程序使用多个单选按钮列表,则需要此控件。
  • 如果您在不同的应用程序中使用单选按钮列表,则可能需要此控件。

1b) 是否有更好的方法?

  • 我认为从ListBoxItemsControl等派生,然后创建样式和模板是创建此控件的唯一方法;因此,没有更好的方法。

2)是否有专业控件(开源或其他),可以让我享受数据绑定的好处而不会带来问题?(我们使用Infragistics和DevExpress,但我不熟悉这些套件提供的所有控件。)

  • 肯定有,就是使用ListBoxEditRadioListBoxEditStyleSettings

回答说明

所有答案都表明创建一个 RadioButtonList 控件是不必要的。然而,如果您需要多个单选按钮列表,当您创建样式、控件模板和数据模板时,您最终会得到一系列可以称为单选按钮列表控件的代码工件集合。因此,在我看来,RadioButtonList 是必需的。
此外,我的理解是,一个 RadioButtonList 在早期的 WPF CTP 中被删除了。这是可以理解的,因为这种很容易创建的控件的需求有限。
关于已接受的答案的评论:
有一个专业控件 - ListBoxEditRadioListBoxEditStyleSettings
最后对 Mike Strobel 的回答进行评论:
我拥有的 RadioButtonList 就是他回答的结果。虽然我擅长创建自定义控件,但我更愿意让 Infragistics 和 DevExpress 等第三方组件制造商创建和支持像这样的基本控件。

你能解释一下它如何帮助数据绑定吗? - jamesSampica
如果您既不提供RadioButtonList的来源,也不描述其确切问题,那么很难进行评判。有些人可能会说真正的WPF方式是使用具有自定义样式和模板的ItemsControl,但这实际上取决于情况。 - Athari
我只能告诉你,DevExpress使用ListBoxEditRadioListBoxEditStyleSettings来表示一组单选按钮。实际上,它与您正在使用的控件几乎相同,但我认为它提供了更好的功能并经过了充分测试。DevExpress不提供RadioButton,在我的应用程序中,我使用WPF/Silverlight提供的默认RadionButton-Control。 - Jehof
@Jehof,很有趣。我需要再仔细审查一下,但是在一些边距和填充调整的情况下,我认为它会起作用。 - AMissico
@Jehof - 创建一个答案,这样我就可以给你赏金了。 - AMissico
显示剩余3条评论
4个回答

4

这个控件真的必要吗?有更好的方法吗?

正如 @lawc 指出的那样,不,它并不是必要的。但是,根据您所需的灵活性水平,它可能更可取。创建可重用的样式很容易,但要做到“正确”,就需要比简单设置自定义 ItemTemplate 更多的工作。

使用样式

在WPF中,ItemsControl会使用适当的容器包装其项。核心WPF中的每个选择器控件都会覆盖确定项是否能够作为其自己的容器的逻辑,以及生成新项容器的工厂代码。例如,ListBox会将其每个项都包装在ListBoxItem中(除非该项本身已经是ListBoxItem)。可以通过父ItemsControlItemContainerStyle属性设置应用于这些容器的样式。这与ItemTemplate属性不同,后者允许您控制容器内部项的外观。更具体地说,它会覆盖应用于容器中的ContentPresenter的内容模板。
由于RadioButton不是从ListBoxItem派生的,因此仅设置ItemTemplate将生成嵌套在ListBoxItem控件中的RadioButton控件列表,这意味着它们仍将具有通常与ListBox控件相关联的选择Chrome,可能还有一些布局和焦点问题。这可能不是您想要的。
相反,覆盖ItemContainerStyle并使用它来分配自定义的ListBoxItem模板,其中嵌入了一个RadioButton。您可能可以完全不设置GroupName属性,从而消除可能的名称冲突。相反,只需在模板化父项的ListBoxItem.IsSelected属性和RadioButton.IsChecked属性之间建立双向绑定即可。
为了方便使用此技术,通常创建一个Style资源(可在整个应用程序中使用),该资源可以应用于适当的ListBox实例,并设置ItemContainerStyle。或者,您可以将容器样式作为全局资源提供,并在ListBox实例上设置它。无论哪种方式,都需要设置属性。 使用自定义控件 尽管WPF布道者经常背诵更喜欢自定义样式而不是自定义控件的哲学,但在实践中,这并不总是方便的。您可能会发现创建一个扩展ListBox控件的RadioButtonList更为方便,然后给它一个默认样式,该样式自动应用上述自定义样式。这使您无需在每个ListBox实例上手动分配列表样式或容器样式,但这并不是很大的优势。
但也许您想对RadioButton项的外观有更多控制。例如,您可能希望:
1. 调整每个RadioButton项“标记”的边距; 2. 调整与内容相对的标记的垂直对齐方式; 3. 支持水平和垂直方向; 4. 自动禁用未选择的RadioButton内容项。
创建自己的实现,很可能是从ListBox派生而来,可以让您轻松添加这些功能,即使在您已经在整个应用程序中使用您的单选列表之后也可以这样做。虽然这也可以使用上面的技术完成,但它可能需要一个附加的行为或一些附加属性,这样您就会得到一个有点分散的设计。
第三方解决方案
“有没有专业的控件,无论是开源还是其他的,可以让我获得数据绑定的好处而没有头痛呢?”
这不是一个罕见的用例,我毫不怀疑有一些实现正在流传。有些可能在开源框架中,有些可能从开源应用程序中提取出来。至于第三方实现,我知道Actipro在其共享WPF库中提供了一个RadioButtonList,该库包含其所有的WPF组件。最后一次我查看时,它不能单独使用。但是,它支持我上面列出的所有附加功能。

1
我只能告诉你,DevExpress使用一个ListBoxEdit和一个RadioListBoxEditStyleSettings来表示一组单选按钮。实际上,它与你正在使用的控件基本相同,但我认为它提供了更好的功能并经过了充分测试。DevExpress不提供RadioButton,在我的应用程序中,我使用WPF / Silverlight提供的默认RadionButton-Control。

您可以按以下方式使用DevExpress的RadioListBoxEdit:

<dxe:ListBoxEdit SelectedItem={Binding CheckItem, Mode=TwoWay}>
  <dxe:ListBoxEdit.StyleSettings>
    <dxe:RadioListBoxEditStyleSettings />
  </dxe:ListBoxEdit.StyleSettings>
</dxe:ListBoxEdit>

你可以在这里找到关于DevExpress的ListBoxEdit更多信息。


1

我认为你不需要这个控件。你可以简单地使用.Net ListBox来实现所有现有的功能。

  1. 使用ListBox.ItemsSource,你可以将选项集合数据绑定
  2. 指定包含RadioButton的ListBox.ItemTemplate,在此模板中,你可以将视图模型属性数据绑定到RadioButton.GroupName

我不认为这是更好的方法,特别是如果我必须重复这个功能一百多次。目前的 RadioButtonList 至少提供了代码重用。 - AMissico
1
您不需要重新编写任何代码。您可以将 ListBox.ItemTemplate 的 DataTemplate 放入资源中,并在整个应用程序中使用它。或者,如果您希望相同的模板应用于整个应用程序,则可以使用此模板定义全局 ListBox 样式。 - Rajiv
是的,但我已经有了一个可以完成所有这些操作的RadioButtonList。如果我不使用这个控件,最终我会得到一个“单选按钮列表”控件。 - AMissico

0

在我看来,从ItemsControl派生的控件将是最干净的方法。

然后,您可能会重写:

  • IsItemItsOwnContainerOverride(),并使用return item is RadioButton;
  • GetContainerForItemOverride(),并使用return为每个项目创建一个new RadioButton(),以及
  • PrepareContainerForItemOverride(),并设置绑定ToggleButton.IsCheckedPropertyContentControl.ContentProperty

虽然这些部分只是样板代码,但在键盘行为的实现中可能需要更多的努力。


最好扩展Selector而不是ItemsControl,主要区别在于前者实际上支持跟踪所选项目。尽管上次我检查时,Selector提供的管道缺少一些美观性(例如,我认为项目上的IsSelected附加属性与父项的SelectedItem属性同步不良)。自.NET 3.0以来,这可能已经得到改善。否则,扩展ListBox可能会节省一些时间/精力。 - Mike Strobel
@MikeStrobel:是的,在_WPF_中,Selector可能是更好的选择。我曾以为它的构造函数像在_Silverlight_中一样是internal,但实际上它是protected,因此您可以子类化Selector - Benjamin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接