WPF 大屏数据可视化:用 LiveCharts 快速实现带标签 + 图例的饼图

在 WPF 数据可视化场景中(如大屏看板、统计报表),饼图是展示数据占比的核心组件。本文基于 LiveCharts.Wpf.NetCore3 图表库,通过 纯 Code-Behind 方式 实现一个功能完整的饼图,包含多品类占比展示、标签定制、图例配置、鼠标悬浮交互,同时附带所有核心参数的详细说明,新手也能快速落地。
image

一、前置准备

 

1. 环境要求

 
  • .NET Core 3.1 及以上版本(或 .NET 5/6/7/8)
  • Visual Studio 2019 及以上
 

2. 安装图表库

 
通过 NuGet 安装核心包(与折线图案例共用同一库,无需重复安装):
 
  1. 右键项目 →「管理 NuGet 程序包」
  2. 搜索 LiveCharts.Wpf.NetCore3 → 安装最新稳定版
 

二、完整实现代码

 

1. XAML 布局(仅定义饼图控件)

 
前台仅需创建饼图容器,所有逻辑通过后台代码控制:
 
xml
 
 
 
 
 
<Window x:Class="LiveChartsPieDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
        Title="饼图参数详解案例" Height="600" Width="900"
        Background="#1E1E1E" Loaded="Window_Loaded">
    <Grid Margin="20">
        <!-- 饼图容器:深色边框+圆角 -->
        <Border Background="#2D2D2D" CornerRadius="8" Padding="20">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="35"/> <!-- 标题行 -->
                    <RowDefinition Height="*"/> <!-- 饼图行 -->
                </Grid.RowDefinitions>

                <!-- 图表标题 -->
                <TextBlock Text="产品品类销售占比分析" 
                           Foreground="White" FontSize="18" 
                           Margin="10,0,0,0" VerticalAlignment="Center"/>

                <!-- 饼图核心控件(命名供后台操作) -->
                <lvc:PieChart x:Name="pieChart" 
                              Grid.Row="1" 
                              Background="Transparent"
                              Hoverable="True"
                              LegendLocation="Right"> <!-- 图例位置(右侧) -->
                </lvc:PieChart>
            </Grid>
        </Border>
    </Grid>
</Window>
 
 

2. Code-Behind 后台逻辑(核心参数配置)

 
所有饼图数据、样式、标签、交互逻辑均在后台实现,无需 MVVM 绑定:
 
csharp
 
运行
 
 
 
 
using LiveCharts;
using LiveCharts.Defaults;
using LiveCharts.Wpf;
using System;
using System.Windows;
using System.Windows.Media;

namespace LiveChartsPieDemo
{
    public partial class MainWindow : Window
    {
        // 定时器:模拟实时数据更新(可选,用于动态调整占比)
        private System.Timers.Timer _updateTimer;

        public MainWindow()
        {
            InitializeComponent();
        }

        // 窗口加载时初始化饼图
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            InitPieChart(); // 初始化饼图配置
            StartRealTimeUpdate(); // 启动实时数据更新(可选)
        }

        /// <summary>
        /// 初始化饼图:配置数据、样式、标签、图例
        /// </summary>
        private void InitPieChart()
        {
            #region 1. 定义饼图系列(核心数据+样式)
            var pieSeriesCollection = new SeriesCollection
            {
                // 品类A:占比35%
                new PieSeries
                {
                    // ========== 数据相关 ==========
                    Title = "品类A", // 品类名称(用于图例+标签)
                    Values = new ChartValues<ObservableValue> { new ObservableValue(35) }, // 占比数值(支持实时更新)
                    DataLabels = true, // 是否显示标签(占比信息)
                    LabelPoint = point => $"{point.Title}\n{point.Y}%", // 标签格式(名称+占比)

                    // ========== 样式相关 ==========
                    Fill = new SolidColorBrush(Colors.Orange), // 饼图扇区颜色
                    Stroke = new SolidColorBrush(Color.FromRgb(30, 30, 30)), // 扇区边框色(区分扇区)
                    StrokeThickness = 2, // 边框宽度
                    Opacity = 0.9, // 透明度(0-1)

                    // ========== 交互相关 ==========
                    HoverPushOut = 15, // 鼠标悬浮时扇区向外偏移距离(视觉突出)
                    IsHitTestVisible = true // 是否响应鼠标事件
                },

                // 品类B:占比25%
                new PieSeries
                {
                    Title = "品类B",
                    Values = new ChartValues<ObservableValue> { new ObservableValue(25) },
                    DataLabels = true,
                    LabelPoint = point => $"{point.Title}\n{point.Y}%",
                    Fill = new SolidColorBrush(Color.FromRgb(144, 238, 144)), // 浅绿
                    Stroke = new SolidColorBrush(Color.FromRgb(30, 30, 30)),
                    StrokeThickness = 2,
                    HoverPushOut = 15
                },

                // 品类C:占比20%
                new PieSeries
                {
                    Title = "品类C",
                    Values = new ChartValues<ObservableValue> { new ObservableValue(20) },
                    DataLabels = true,
                    LabelPoint = point => $"{point.Title}\n{point.Y}%",
                    Fill = new SolidColorBrush(Colors.Purple),
                    Stroke = new SolidColorBrush(Color.FromRgb(30, 30, 30)),
                    StrokeThickness = 2,
                    HoverPushOut = 15
                },

                // 品类D:占比20%
                new PieSeries
                {
                    Title = "品类D",
                    Values = new ChartValues<ObservableValue> { new ObservableValue(20) },
                    DataLabels = true,
                    LabelPoint = point => $"{point.Title}\n{point.Y}%",
                    Fill = new SolidColorBrush(Colors.Red),
                    Stroke = new SolidColorBrush(Color.FromRgb(30, 30, 30)),
                    StrokeThickness = 2,
                    HoverPushOut = 15
                }
            };
            #endregion

            #region 2. 绑定系列+配置标签样式
            // 将饼图系列添加到图表
            pieChart.Series = pieSeriesCollection;

            // 全局标签样式配置(统一控制所有标签)
            foreach (var series in pieChart.Series)
            {
                var pieSeries = (PieSeries)series;
                pieSeries.LabelPosition = PieLabelPosition.Outline; // 标签位置(Outline/Inside/Outside)
                pieSeries.LabelForeground = Brushes.White; // 标签文字颜色
                pieSeries.LabelFontSize = 12; // 标签字体大小
                pieSeries.LabelPadding = 5; // 标签与扇区的间距
            }
            #endregion

            #region 3. 配置图例样式
            pieChart.Legend = new DefaultLegend
            {
                Foreground = Brushes.White, // 图例文字颜色
                FontSize = 14, // 图例字体大小
                Padding = new Thickness(10), // 图例内边距
                Margin = new Thickness(20, 0, 0, 0), // 图例外边距
                BulletSize = 12, // 图例前小方块大小
                Orientation = System.Windows.Controls.Orientation.Vertical, // 图例排列方向(垂直)
                HorizontalAlignment = HorizontalAlignment.Left,
                VerticalAlignment = VerticalAlignment.Center
            };
            #endregion

            #region 4. 饼图全局配置
            pieChart.DisableAnimations = false; // 是否禁用动画(大数据量建议开启)
            pieChart.AnimationSpeed = TimeSpan.FromMilliseconds(800); // 加载动画时长
            pieChart.InnerRadius = 60; // 内环半径(0=普通饼图,>0=环形图)
            pieChart.Hoverable = true; // 是否支持鼠标悬浮交互
            pieChart.DataTooltip = new DefaultTooltip // 自定义悬浮提示框
            {
                Background = Brushes.DarkSlateGray,
                Foreground = Brushes.White,
                FontSize = 13,
                Padding = new Thickness(10),
                CornerRadius = new CornerRadius(4),
                TooltipPosition = TooltipPosition.Top // 提示框位置
            };
            #endregion
        }

        /// <summary>
        /// 模拟实时数据更新(业务场景可替换为接口拉取/MQ推送)
        /// </summary>
        private void StartRealTimeUpdate()
        {
            _updateTimer = new System.Timers.Timer(5000); // 每5秒更新一次
            _updateTimer.Elapsed += (s, e) =>
            {
                Dispatcher.Invoke(() =>
                {
                    // 随机调整某个品类的占比(保持总占比100%,这里简化处理)
                    var random = new Random();
                    var targetSeriesIndex = random.Next(0, pieChart.Series.Count);
                    var targetSeries = (PieSeries)pieChart.Series[targetSeriesIndex];
                    var currentValue = targetSeries.Values[0].Value;

                    // 随机增减1-3个百分点(避免占比为负或超过50%)
                    var change = random.Next(-3, 4);
                    var newValue = Math.Clamp(currentValue + change, 5, 50);

                    // 更新数据(ObservableValue 支持实时刷新)
                    targetSeries.Values[0].Value = newValue;

                    // 调整其他品类占比(简化逻辑:让最后一个品类承担差值)
                    var lastSeries = (PieSeries)pieChart.Series[pieChart.Series.Count - 1];
                    var totalChange = currentValue - newValue;
                    lastSeries.Values[0].Value = Math.Clamp(lastSeries.Values[0].Value + totalChange, 5, 50);
                });
            };
            _updateTimer.Start();
        }

        // 窗口关闭时释放定时器(防止内存泄漏)
        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            _updateTimer?.Stop();
            _updateTimer?.Dispose();
        }
    }
}
 
 

三、核心参数详细说明(新手必看)

 

1. PieSeries 饼图系列参数(核心)

 
参数 类型 作用 可选值 / 推荐示例
Title string 扇区名称(用于图例 + 标签) "品类A"/"产品1"
Values ChartValues<ObservableValue> 占比数据(支持实时更新) new ChartValues<ObservableValue> { new ObservableValue(35) }
DataLabels bool 是否显示扇区标签 true(显示)/false(隐藏)
LabelPoint Func<ChartPoint, string> 标签格式化逻辑 point => $"{point.Title}\n{point.Y}%"(名称 + 占比)
LabelPosition PieLabelPosition 标签位置 Outline(扇区边缘)/Inside(扇区内)/Outside(扇区外)
Fill Brush 扇区填充色 Brushes.Orange/new SolidColorBrush(Colors.LightGreen)
Stroke Brush 扇区边框色 new SolidColorBrush(Color.FromRgb(30, 30, 30))(深色边框区分扇区)
StrokeThickness int 边框宽度 2(常规)/3(大屏)
HoverPushOut int 鼠标悬浮偏移距离 10-15(视觉突出,大屏建议 15)
Opacity double 扇区透明度 0.9(常规)/0.7(半透明)
 

2. PieChart 全局参数

 
参数 类型 作用 推荐配置
Series SeriesCollection 饼图系列集合(多个扇区) new SeriesCollection { series1, series2, ... }
Legend DefaultLegend 图例样式配置 白色文字 + 垂直排列 + 右侧位置
InnerRadius int 内环半径(环形图关键参数) 0(普通饼图)/60-100(环形图,大屏建议 100)
DisableAnimations bool 禁用加载 / 更新动画 false(常规)/true(大数据量)
AnimationSpeed TimeSpan 动画时长 TimeSpan.FromMilliseconds(800)(加载流畅)
LegendLocation LegendLocation 图例位置 Right(右侧)/Bottom(底部)/Left(左侧)
DataTooltip DefaultTooltip 悬浮提示框样式 深色背景 + 白色文字 + 圆角 + 内边距
Hoverable bool 是否支持鼠标悬浮交互 true(支持)/false(禁用)
 

3. DefaultLegend 图例参数

 
参数 类型 作用 推荐示例
Foreground Brush 图例文字颜色 Brushes.White(适配深色背景)
FontSize int 图例字体大小 14-16(大屏)/12(常规)
BulletSize int 图例前小方块大小 12-14(与字体大小匹配)
Orientation Orientation 图例排列方向 Vertical(垂直,节省空间)/Horizontal(水平)
Margin/Padding Thickness 外边距 / 内边距 Margin="20,0,0,0"(与饼图保持间距)
 

四、运行效果与优化建议

 

1. 运行效果

 
  • 深色背景适配大屏场景,4 个扇区展示不同品类占比,环形设计更美观;
  • 扇区带边框区分,鼠标悬浮时扇区向外偏移,视觉突出;
  • 标签显示品类名称 + 占比,图例在右侧垂直排列,清晰易读;
  • 每 5 秒自动更新占比数据,动画过渡流畅,无卡顿。
 

2. 大屏适配优化(重点)

 
  1. 尺寸放大StrokeThickness 设为 3,LabelFontSize 设为 16,Legend.FontSize 设为 16,BulletSize 设为 14;
  2. 环形图优化InnerRadius 设为 100-150(增大内环,提升大屏视觉效果);
  3. 交互增强HoverPushOut 设为 15-20(悬浮偏移更明显,远距离可见);
  4. 全屏显示:设置窗口 WindowStyle="None" + WindowState="Maximized",添加 ESC 退出逻辑(同折线图案例);
  5. 颜色适配:采用高对比度颜色(如橙色、浅绿、紫色、红色),避免与深色背景冲突,同时确保扇区颜色不重复。
 

五、常见问题解决

 
  1. 标签重叠或显示不全?
     
    方案 1:调整 LabelPositionOutline(边缘显示);方案 2:增大 LabelPadding(标签间距);方案 3:减少标签文字长度。
     
  2. 实时更新数据后饼图不刷新?
     
    必须使用 ObservableValue 作为数据类型(而非 double),该类型自带变更通知,支持实时刷新。
     
  3. 图例与饼图重叠?
     
    调整 Legend.Margin(如 Margin="50,0,0,0"),增加图例与饼图的间距,或修改 LegendLocationBottom(底部显示)。
     
  4. 扇区颜色太暗,大屏看不清?
     
    选择高亮度、高对比度颜色(如 Colors.LightSeaGreenColors.Gold),或降低 Opacity 至 0.8-0.9,提升视觉辨识度。
     
 

六、扩展场景

 
  1. 多环饼图:添加多个 PieChart 控件叠加,设置不同 InnerRadius,实现嵌套环形图;
  2. 数据筛选:添加下拉框选择时间范围,通过修改 Series.Values 实现占比数据筛选;
  3. 点击事件:给 PieSeries 添加 DataClick 事件,点击扇区弹窗显示该品类详细数据:
    csharp
     
    运行
     
     
     
     
    pieSeries.DataClick += (s, args) =>
    {
        MessageBox.Show($"品类:{args.Series.Title}\n占比:{args.ChartPoint.Y}%", "品类详情");
    };
     
     
  4. 导出功能:结合 System.Windows.Media.Imaging,将饼图导出为图片(PNG/JPG)。
 

 

总结

 
本文基于 LiveCharts.Wpf.NetCore3 实现了一个功能完整的 WPF 饼图,采用 Code-Behind 方式,无需复杂的 MVVM 绑定,新手可快速上手。核心亮点:
 
  • 支持环形图 / 普通饼图切换,标签、图例全定制;
  • 支持实时数据更新和鼠标悬浮交互,体验更佳;
  • 附带详细参数说明和大屏适配方案,实用性强。
posted @ 2026-01-20 13:25  郭小俊  阅读(65)  评论(0)    收藏  举报