惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
Scott Helme
Scott Helme
NISL@THU
NISL@THU
Cisco Talos Blog
Cisco Talos Blog
C
Cybersecurity and Infrastructure Security Agency CISA
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
J
Java Code Geeks
U
Unit 42
The GitHub Blog
The GitHub Blog
H
Help Net Security
T
Tenable Blog
aimingoo的专栏
aimingoo的专栏
Jina AI
Jina AI
Spread Privacy
Spread Privacy
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
L
Lohrmann on Cybersecurity
T
Threatpost
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Engineering at Meta
Engineering at Meta
A
About on SuperTechFans
I
InfoQ
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog
L
LINUX DO - 最新话题
K
Kaspersky official blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Threat Research - Cisco Blogs
C
Check Point Blog
T
The Blog of Author Tim Ferriss
有赞技术团队
有赞技术团队
宝玉的分享
宝玉的分享
Help Net Security
Help Net Security
Google DeepMind News
Google DeepMind News
A
Arctic Wolf
Y
Y Combinator Blog
N
News | PayPal Newsroom
M
MIT News - Artificial intelligence
Latest news
Latest news
H
Hacker News: Front Page
Blog — PlanetScale
Blog — PlanetScale
腾讯CDC
I
Intezer
爱范儿
爱范儿
F
Fortinet All Blogs
P
Palo Alto Networks Blog
C
CERT Recently Published Vulnerability Notes

博客园 - Hey,Coder!

Elasticsearch ILM 与 Data Stream 概念及实例说明文档 portainer httphelper封装 Ocelot + Consul + SignalR 粘性会话 正交软件架构 docker postgresql17 主从复制 rabbitmq总结与c#示例 docker rabbitmq quartz dashboard docker consul registrator 服务自动注册consul 微服务动态扩容 ubuntu24.04 安装docker RAG 检索增强生成 软考 - 架构设计师 知识点总结 c# MailKit3.4.3 发送邮件、附件 nginx 反向代理postgresql c# 信号量 elsa 3.5 中间件记录最后执行的节点数据 xp密钥 c# System.Text.Json 反序列化Dictionary<string,object>时未转换基础类型的处理方法 docker配置代理 openclaw 接入 LMStudio的模型服务 wpf canvas 移动 缩放 windows平台openclaw搭建 wpf scrollerview触摸滚动 c# 动态切换sqlsugar连接字符串 c# Elastic.Clients.Elasticsearch 动态查询 c# es 封装Elastic.Clients.Elasticsearch c# scrollerview滚动到指定元素位置 基于elsa工作流封装一套变量、组件的体系 常用活动重写 DynamicExpresso 轻量级动态表达式库 c# elsa 3.5.2 程序化工作流常用功能及自定义中间件 leaflet docker 连接老版本的sqlserver ssl错误 c# quartz 动态创建任务 控制任务运行和停止 依赖注入 cefsharp 模拟点击 批量重置数据库的数据 docker 复制远程镜像本地并创建容器 c# newtonsoft dynamic
WPF 重写Expander
Hey,Coder! · 2026-02-12 · via 博客园 - Hey,Coder!

效果图
image

<UserControl x:Class="BlackBoard.UserControls.ColorExpander"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:BlackBoard.UserControls"
             mc:Ignorable="d"              x:Name="rootControl"
             xmlns:usercontrols="clr-namespace:BlackBoard.UserControls"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <!-- 定义统一的背景色变量 -->
        <SolidColorBrush x:Key="HeaderBackgroundBrush" Color="#D4F7EA"/>

        <Style x:Key="ExpanderStyle" TargetType="{x:Type Expander}">
            <Setter Property="IsExpanded" Value="False"></Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Expander}">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="auto"></RowDefinition>
                                <RowDefinition Height="auto"></RowDefinition>
                            </Grid.RowDefinitions>

                            <Border BorderThickness="0" Background="{DynamicResource HeaderBackgroundBrush}" >
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="auto"/>
                                        <ColumnDefinition/>
                                    </Grid.ColumnDefinitions>
                                    <!-- 将ToggleButton放在第一行,覆盖整个Header区域 -->
                                    <ToggleButton x:Name="HeaderSite" Grid.ColumnSpan="2" Background="Transparent" Margin="0" Padding="0"
                                      IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"                                  
                                      Style="{DynamicResource ExpanderToggleButtonStyle}" >
                                        <ToggleButton.Content>
                                            <!-- 在ToggleButton内部放置Header内容 -->
                                            <DockPanel HorizontalAlignment="Stretch" Background="{DynamicResource HeaderBackgroundBrush}">
                                                <!-- 小三角图标容器,设置与Header相同的背景色 -->
                                                <Border Width="20" Background="{DynamicResource HeaderBackgroundBrush}" DockPanel.Dock="Left">
                                                    <Canvas Width="12" Height="12" VerticalAlignment="Center" HorizontalAlignment="Center">
                                                        <!-- 使用RenderTransform实现旋转效果 -->
                                                        <Path x:Name="expanderArrow"
                                                          Data="M0,0 L8,4 L0,8 Z" 
                                                          Stroke="Black" 
                                                          StrokeThickness="2"
                                                          Fill="Black"
                                                          Canvas.Left="2"
                                                          Canvas.Top="2"
                                                          RenderTransformOrigin="0.5,0.5">
                                                            <Path.RenderTransform>
                                                                <RotateTransform Angle="0"/>
                                                            </Path.RenderTransform>
                                                        </Path>
                                                    </Canvas>
                                                </Border>

                                                <!-- 实际Header内容 -->
                                                <ContentPresenter 
                                                ContentTemplate="{TemplateBinding HeaderTemplate}" 
                                                Content="{TemplateBinding Header}" 
                                                VerticalAlignment="Center"
                                                Margin="0,0,0,0"/>
                                            </DockPanel>
                                        </ToggleButton.Content>
                                    </ToggleButton>
                                </Grid>
                            </Border>
                            <ContentPresenter x:Name="ExpandSite" Grid.Row="1" Margin="0"
                                          ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" Focusable="False" 
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Visibility="Collapsed" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsExpanded" Value="True">
                                <Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
                                <!-- 当展开时,旋转小三角90度 -->
                                <Setter TargetName="expanderArrow" Property="RenderTransform">
                                    <Setter.Value>
                                        <RotateTransform Angle="90"/>
                                    </Setter.Value>
                                </Setter>
                            </Trigger>
                            <Trigger Property="IsExpanded" Value="False">
                                <!-- 当收起时,恢复原始角度 -->
                                <Setter TargetName="expanderArrow" Property="RenderTransform">
                                    <Setter.Value>
                                        <RotateTransform Angle="0"/>
                                    </Setter.Value>
                                </Setter>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Key="ExpanderToggleButtonStyle" TargetType="{x:Type ToggleButton}">
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Foreground" Value="Gainsboro"/>
            <Setter Property="FontSize" Value="15" />
            <Setter Property="FontFamily" Value="宋体" />
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                        <Grid Background="Transparent">
                            <ContentPresenter 
                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                            Margin="{TemplateBinding Padding}" 
                            RecognizesAccessKey="True"                       
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Grid>

                        <ControlTemplate.Triggers>
                            <Trigger Property="IsPressed" Value="true">
                                <!-- 鼠标按下时,可以添加轻微的透明度变化来表示交互 -->
                                <Setter Property="Opacity" Value="0.9"/>
                            </Trigger>
                            <Trigger Property="IsChecked" Value="true">
                                <!-- 这里通过动画或样式更改来显示展开状态 -->
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="Gray"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </UserControl.Resources>
    <Grid>
        <Expander HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Padding="0" Margin="0" 
      Style="{StaticResource ExpanderStyle}">
            <Expander.Header>
                <DockPanel HorizontalAlignment="Stretch" Background="{DynamicResource HeaderBackgroundBrush}" Margin="0" Height="30" >
                    <StackPanel Orientation="Horizontal" DockPanel.Dock="Left" HorizontalAlignment="Stretch" VerticalAlignment="Center">
                        <TextBlock Name="lblTitle" Text="" Margin="5,0" FontSize="12" FontWeight="Bold"/>
                    </StackPanel>
                </DockPanel>
            </Expander.Header>
            <ContentPresenter Name="content"/>
        </Expander>
    </Grid>
</UserControl>

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace BlackBoard.UserControls
{
    /// <summary>
    /// ColorExpander.xaml 的交互逻辑
    /// </summary>
    public partial class ColorExpander : UserControl
    {
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Title.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register(nameof(Title), typeof(string), typeof(ColorExpander), new PropertyMetadata(string.Empty, OnTitleChange));

        private static void OnTitleChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (ColorExpander)d;
            if (e.NewValue is string value)
            {
                control.Dispatcher.Invoke(() =>
                {
                    control.lblTitle.Text = value;
                });
            }
        }

        public string BGColor
        {
            get { return (string)GetValue(ColorProperty); }
            set { SetValue(ColorProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Color.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ColorProperty =
            DependencyProperty.Register(nameof(BGColor), typeof(string), typeof(ColorExpander), new PropertyMetadata(string.Empty, OnColorChange));

        private static void OnColorChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (ColorExpander)d;
            if (e.NewValue is string value)
            {
                control.Dispatcher.Invoke(() =>
                {
                    if (!string.IsNullOrEmpty(value))
                    {
                        // 获取当前的SolidColorBrush
                        var brush = (SolidColorBrush)control.FindResource("HeaderBackgroundBrush");
                        if (brush != null)
                        {
                            brush.Color = (Color)ColorConverter.ConvertFromString(value);
                        }
                    }
                });
            }
        }



        public object Body
        {
            get { return (object)GetValue(BodyProperty); }
            set { SetValue(BodyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Body.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BodyProperty =
            DependencyProperty.Register(nameof(Body), typeof(object), typeof(ColorExpander), new PropertyMetadata(null, OnBodyChange));

        private static void OnBodyChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (ColorExpander)d;
            if (e.NewValue is object value)
            {
                control.Dispatcher.Invoke(() =>
                {
                    control.content.Content = value;
                });
            }
        }

        public ColorExpander()
        {
            InitializeComponent();
        }
    }
}

使用

<usercontrols:ColorExpander BGColor="#FF9F3A" Title="A  [ 3人 / 占 12.5% ]">
    <usercontrols:ColorExpander.Body>
        <ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="200">
            <StackPanel Orientation="Horizontal" Margin="0 10">
                <Border Background="#F5F5F5" CornerRadius="3" Padding="8" Margin="0,0,8,5">
                    <TextBlock Text="张三张三" Foreground="#333"/>
                </Border>
                <Border Background="#F5F5F5" CornerRadius="3" Padding="8" Margin="0,0,8,5">
                    <TextBlock Text="李四" Foreground="#333"/>
                </Border>
                <Border Background="#F5F5F5" CornerRadius="3" Padding="8" Margin="0,0,8,5">
                    <TextBlock Text="王五" Foreground="#333"/>
                </Border>
            </StackPanel>
        </ScrollViewer>
    </usercontrols:ColorExpander.Body>
</usercontrols:ColorExpander>