当前位置: 首页 > news >正文

WPF——自定义ListBox

在阅读本文前,最好先看看WPF——自定义RadioButton

背景

WPF中实现单选功能通常有两种方案:
- **RadioButton组**:传统方案,但代码冗余
- **ListBox定制**:通过样式改造,兼顾数据绑定和UI灵活性

需求

一组选项中,选中某个选项(选项需要横向排列,同时选中效果与未选中效果要能明确显示),就将这个选项的值写入到后端。

设计选型

RadioButton方案

通过RadioButton来实现,是肯定可行的,

但是对于一组RadioButton,需要设置组名;

同时每个RadioButton在Checked时都要触发事件,也需要为它们设置相应的事件,不利用后台绑定:也就是说要将选中值写入VM比较麻烦,因为所有的都需要实现;

并且还存在一个问题,所有RadioButton需要手动去设置相应的显示内容,不能使用一个集合以便于管理。

ListBox方案

通过ListBox也是可行的,只是它就需要进行一些改造了,因为ListBox默认的是纵向排列,同时它没有一个明确的选中与未选中效果;

但是它可以绑定集合,同时通过自定义,可以比较轻松的将选中值写入到VM中;或者通过item的选中事件即可将选中值写入到VM中。

方案对比

方案代码量数据绑定布局灵活性维护成本
RadioButton组困难
ListBox定制简单
 

后续基于ListBox进行实现。

实现

样式定义

 <Style x:Key="RadioListBoxStyle" TargetType="{x:Type ListBox}"><!--  基础样式继承原有ListBoxStyle1  --><Setter Property="Background" Value="{StaticResource ListBox.Static.Background}" /><Setter Property="BorderBrush" Value="{StaticResource ListBox.Static.Border}" /><Setter Property="BorderThickness" Value="1" /><Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" /><Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" /><Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" /><Setter Property="ScrollViewer.CanContentScroll" Value="true" /><Setter Property="ScrollViewer.PanningMode" Value="Both" /><Setter Property="Stylus.IsFlicksEnabled" Value="False" /><Setter Property="VerticalContentAlignment" Value="Center" /><!--  关键修改1:禁用默认选中效果  --><Setter Property="ItemContainerStyle"><Setter.Value><Style TargetType="{x:Type ListBoxItem}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type ListBoxItem}"><Border Background="Transparent"><ContentPresenter /></Border></ControlTemplate></Setter.Value></Setter></Style></Setter.Value></Setter><!--  ItemsPanel为水平布局  --><Setter Property="ItemsPanel"><Setter.Value><ItemsPanelTemplate>  <!--  改为水平排列  --><StackPanel Orientation="Horizontal" /></ItemsPanelTemplate></Setter.Value></Setter><!--  自定义ItemTemplate模拟RadioButton  --><Setter Property="ItemTemplate"><Setter.Value><DataTemplate><DockPanel LastChildFill="True"><!--  RadioButton部分  --><RadioButtonMargin="5,0,10,0"VerticalAlignment="Center"DockPanel.Dock="Left"Focusable="False"IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" /><!--  文本部分  --><TextBlockMargin="0,0,5,0"VerticalAlignment="Center"Text="{Binding}" /></DockPanel></DataTemplate></Setter.Value></Setter><!--  模板  --><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type ListBox}"><Borderx:Name="Bd"Padding="1"Background="{TemplateBinding Background}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"SnapsToDevicePixels="true"><ScrollViewer Padding="{TemplateBinding Padding}" Focusable="false"><ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /></ScrollViewer></Border><ControlTemplate.Triggers><Trigger Property="IsEnabled" Value="false"><Setter TargetName="Bd" Property="Background" Value="{StaticResource ListBox.Disabled.Background}" /><Setter TargetName="Bd" Property="BorderBrush" Value="{StaticResource ListBox.Disabled.Border}" /></Trigger><MultiTrigger><MultiTrigger.Conditions><Condition Property="IsGrouping" Value="true" /><Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" /></MultiTrigger.Conditions><Setter Property="ScrollViewer.CanContentScroll" Value="false" /></MultiTrigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style>

数据绑定

    <Window.DataContext><local:Tests /></Window.DataContext>

Tests如下,以下可以根据需要自行实现通知属性。

public class Tests:INotifyPropertyChanged
{readonly List<string> testDatas = ["test0","test1","test2","test3","test4",];private string _selectedValue;public string SelectedValue{get => _selectedValue;set { _selectedValue = value; OnPropertyChanged(); }}public List<string> TestDatas { get; set; }public Tests(){TestDatas = testDatas;}//INotifyPropertyChanged实现
}

ListBox如下:

<ListBoxMargin="0,183,462,192"ItemsSource="{Binding TestDatas}"SelectedItem="{Binding SelectedValue, Mode=TwoWay}"Style="{DynamicResource RadioListBoxStyle}" />

效果展示

图:横向排列的单选ListBox,左侧为RadioButton,右侧为文本

http://www.lryc.cn/news/594726.html

相关文章:

  • C++ - 仿 RabbitMQ 实现消息队列--服务端核心模块实现(二)
  • 学习秒杀系统-异步下单(包含RabbitMQ基础知识)
  • ASP.NET Core Web API 中集成 DeveloperSharp.RabbitMQ
  • 关于校准 ARM 开发板时间的步骤和常见问题:我应该是RTC电池没电了才导致我设置了重启开发板又变回去2025年的时间
  • Android NDK ffmpeg 音视频开发实战
  • 什么是“差分“?
  • 包装类简单了解泛型
  • 图片转 PDF三个免费方法总结
  • 支持不限制大小,大文件分段批量上传功能(不受nginx /apache 上传大小限制)
  • 网络设备功能对照表
  • 【Spark征服之路-3.6-Spark-SQL核心编程(五)】
  • Linux 文件操作详解:结构、系统调用、权限与实践
  • 第二阶段-第二章—8天Python从入门到精通【itheima】-134节(SQL——DQL——分组聚合)
  • leetcode-sql-627变更性别
  • 深入解析IP协议:组成、地址管理与路由选择
  • Tomato靶机通关教程
  • 安装docker可视化工具 Portainer中文版(ubuntu上演示,所有docker通用) 支持控制各种容器,容器操作简单化 降低容器门槛
  • 板凳-------Mysql cookbook学习 (十二--------4)
  • 技能学习PostgreSQL中级专家
  • 借助AI学习开源代码git0.7之六write-cache
  • 基于 STM32 的数字闹钟系统 Proteus 仿真设计与实现
  • 从一开始的网络攻防(六):php反序列化
  • 金仓数据库:融合进化,智领未来——2025年数据库技术革命的深度解析
  • STM32 USB键盘实现指南
  • 最严电动自行车新规,即将实施!
  • FreeSwitch通过Websocket(流式双向语音)对接AI实时语音大模型技术方案(mod_ppy_aduio_stream)
  • 朝歌智慧盘古信息:以IMS MOM V6重构国产化智能终端新生态
  • 【初识数据结构】CS61B中的最小生成树问题
  • Car Kit重构车机开发体验,让车载应用开发驶入快车道
  • 【PTA数据结构 | C语言版】拓扑排序