功能更新
This commit is contained in:
130
Melskin/Controls/Accordion.xaml.cs
Normal file
130
Melskin/Controls/Accordion.xaml.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
namespace Melskin.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Accordion 控件提供了一个可折叠的面板容器,允许用户通过点击标题来展开或收起内容。
|
||||
/// 该控件继承自 ItemsControl,因此可以使用数据绑定来动态添加 AccordionItem 子项。
|
||||
/// 每个 AccordionItem 可以独立地设置其标题和是否默认展开的状态。
|
||||
/// </summary>
|
||||
public class Accordion : ItemsControl
|
||||
{
|
||||
static Accordion()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(Accordion), new FrameworkPropertyMetadata(typeof(Accordion)));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsItemItsOwnContainerOverride(object item)
|
||||
{
|
||||
return item is AccordionItem;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override DependencyObject GetContainerForItemOverride()
|
||||
{
|
||||
return new AccordionItem();
|
||||
}
|
||||
|
||||
///// <inheritdoc />
|
||||
//protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
//{
|
||||
// base.OnItemsChanged(e);
|
||||
|
||||
// if (e.NewItems != null)
|
||||
// {
|
||||
// // 注意:这里需要确保 e.NewItems 中的项是 AccordionItem 类型
|
||||
// // 当通过 ItemsSource 绑定时,它们最初是数据项,在 GetContainerForItemOverride 之后才被包装
|
||||
// // 因此,事件处理最好在容器生成后进行。
|
||||
// // 为了简单起见,我们在此处添加事件,但这依赖于 ItemsSource 中的项已经是 AccordionItem。
|
||||
// // 一个更健壮的方法是重写 PrepareContainerForItemOverride。
|
||||
// foreach (AccordionItem item in e.NewItems)
|
||||
// {
|
||||
// item.PreviewMouseLeftButtonDown += AccordionItem_PreviewMouseLeftButtonDown;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (e.OldItems != null)
|
||||
// {
|
||||
// foreach (AccordionItem item in e.OldItems)
|
||||
// {
|
||||
// item.PreviewMouseLeftButtonDown -= AccordionItem_PreviewMouseLeftButtonDown;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 重写此方法是处理数据绑定时添加事件的更可靠方式
|
||||
/// </summary>
|
||||
/// <param name="element"></param>
|
||||
/// <param name="item"></param>
|
||||
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
|
||||
{
|
||||
base.PrepareContainerForItemOverride(element, item);
|
||||
if (element is AccordionItem accordionItem)
|
||||
{
|
||||
accordionItem.PreviewMouseLeftButtonDown -= AccordionItem_PreviewMouseLeftButtonDown; // 防止重复添加
|
||||
accordionItem.PreviewMouseLeftButtonDown += AccordionItem_PreviewMouseLeftButtonDown;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
|
||||
{
|
||||
if (element is AccordionItem accordionItem)
|
||||
{
|
||||
accordionItem.PreviewMouseLeftButtonDown -= AccordionItem_PreviewMouseLeftButtonDown;
|
||||
}
|
||||
|
||||
base.ClearContainerForItemOverride(element, item);
|
||||
}
|
||||
|
||||
private void AccordionItem_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is AccordionItem clickedItem)
|
||||
{
|
||||
// 获取点击源的模板父级(即该视觉元素属于哪个控件)
|
||||
var originalSource = e.OriginalSource as FrameworkElement;
|
||||
var parentControl = originalSource?.TemplatedParent as System.Windows.Controls.Primitives.ToggleButton;
|
||||
|
||||
// 【核心修复】:判断是否为有效的标题点击
|
||||
// 1. 必须是 ToggleButton (HeaderButton 是 ToggleButton)
|
||||
// 2. 必须 不是 CheckBox (排除内容里的复选框)
|
||||
// 3. 必须 不是 RadioButton (排除内容里的单选框)
|
||||
bool isHeaderClick = parentControl != null
|
||||
&& parentControl is not System.Windows.Controls.CheckBox
|
||||
&& parentControl is not System.Windows.Controls.RadioButton;
|
||||
|
||||
// 只有确信点击的是标题栏(HeaderButton)时,才由 Accordion 接管处理
|
||||
if (isHeaderClick)
|
||||
{
|
||||
if (clickedItem.IsExpanded)
|
||||
{
|
||||
clickedItem.IsExpanded = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 折叠其他所有项
|
||||
// 注意:ItemContainerGenerator 可能只包含已生成的容器,直接遍历 Items 有时更安全,
|
||||
// 但如果用了虚拟化,ContainerFromIndex 可能会返回 null。
|
||||
// 对于 Accordion 通常项数不多,直接遍历 Items 对应的 Container 即可。
|
||||
foreach (var item in this.Items)
|
||||
{
|
||||
if (this.ItemContainerGenerator.ContainerFromItem(item) is AccordionItem container && container != clickedItem)
|
||||
{
|
||||
container.IsExpanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
clickedItem.IsExpanded = true;
|
||||
}
|
||||
|
||||
// 标记事件已处理,防止事件继续冒泡触发 ToggleButton 自身的点击行为
|
||||
// (因为我们在这里手动切换了 IsExpanded,如果 ToggleButton 再处理一次可能会抵消)
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
// 如果不是 Header 点击(例如点了 CheckBox),什么都不做,让事件继续传播,
|
||||
// 这样 CheckBox 就能接收到点击并正常勾选了。
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user