绘制饼图

时间:2019-12-12 12:45:22

标签: wpf canvas pie-chart dependency-properties

在查看了创建饼图的几个示例之后,我为此做了以下UserControl

public partial class PieChart : UserControl
{
    #region DependencyProperties
    public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double), typeof(PieChart), new PropertyMetadata(0d));
    public static readonly DependencyProperty SeriesProperty = DependencyProperty.Register("Series", typeof(List<PieSeries>), typeof(PieChart), new PropertyMetadata(null, Draw));

    public double Radius {
        get { return (double)GetValue(RadiusProperty); }
        set { SetValue(RadiusProperty, value); }
    }

    public List<PieSeries> Series {
        get { return (List<PieSeries>)GetValue(SeriesProperty); }
        set { SetValue(SeriesProperty, value); }
    }

    static void Draw(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as PieChart;
        control.AddPie(control.chartArea);
    }
    #endregion

    Brush[] colors = new Brush[] { Brushes.Gray, Brushes.Green, Brushes.Blue, Brushes.LightGray, Brushes.AntiqueWhite };
    double xCenter, yCenter;
    public PieChart()
    {
        InitializeComponent();
    }

    void AddPie(Canvas canvas)
    {
        canvas.Width = 300; canvas.Height = 300;
        xCenter = canvas.Width / 2;
        yCenter = canvas.Height / 2;

        double sum, startAngle, sweepAngle;
        sum = Series.Sum(x => x.Value);
        startAngle = sweepAngle = 0.0;

        for (int i = 0; i < Series.Count; i++)
        {
            var brush = colors[i];
            startAngle += sweepAngle;
            sweepAngle = 2 * Math.PI * Series[i].Value / sum;
            DrawSegments(canvas, brush, startAngle, startAngle + sweepAngle);
        }
    }

    void DrawSegments(Canvas canvas, Brush fillColor, double startAngle, double endAngle)
    {
        var line1 = new LineSegment() { Point = new Point(xCenter + Radius * Math.Cos(startAngle), yCenter + Radius * Math.Sin(startAngle)) };
        var line2 = new LineSegment() { Point = new Point(xCenter + Radius * Math.Cos(endAngle), yCenter + Radius * Math.Sin(endAngle)) };
        var arc = new ArcSegment()
        {
            SweepDirection = SweepDirection.Clockwise,
            Size = new Size(Radius, Radius),
            Point = new Point(xCenter + Radius * Math.Cos(endAngle), yCenter + Radius * Math.Sin(endAngle))
        };
        var figure = new PathFigure() { IsClosed = true, StartPoint = new Point(xCenter, yCenter), Segments = { line1, arc, line2 } };
        var geometry = new PathGeometry() { Figures = { figure } };
        var path = new Path() { Fill = fillColor, Data = geometry };
        canvas.Children.Add(path);
    }
}

xaml中有此内容:

<UserControl ...>
<Grid>
        <Canvas x:Name="chartArea" Margin="10"/>
    </Grid>
</UserControl>

不确定这样做是否正确,但是否可行。问题在于AddPie方法中。我必须设置Width的{​​{1}}和Height,否则Canvas中什么也没有出现。这是我在MainWindow中使用它的方式:

MainWindow

在ViewModel的<Window> <Grid Margin="5"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*" /> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <local:PieChart Grid.Row="1" Radius="100" Series="{Binding Series}"/> </Grid> </Window> 中,我创建了constructor

Series
Series = new List<PieSeries>() { new PieSeries("A", 30), new PieSeries("B", 20), new PieSeries("C", 10), new PieSeries("D", 15), new PieSeries("E", 25) }; 回调中,

我总是得到Draw作为0ActualHeight的{​​{1}}和ActualWidth的名字,{Pet}调整窗口大小时会自动调整大小!

该如何解决?

是否有更好的方法绘制简单的饼图?就我而言,Canvas中可能有1到5项。

编辑

使用注释中建议的方法,它要简单得多!在我的ViewModel chartArea中,这些是:

List<PieSeries>

VM中的

public class VM : INotifyPropertyChanged
{
    public ObservableCollection<ShapeData> Series { get; set; } = new ObservableCollection<ShapeData>();
    double[] array = { 30, 10, 15, 20, 25};
    Brush[] brushes = new Brush[] { Brushes.Gray, Brushes.Green, Brushes.Blue, Brushes.LightGray, Brushes.AntiqueWhite };
    double radius, xCenter, yCenter;

    public Command Resized { get; set; }
    public VM()
    {
        radius = 100;
        Resized = new Command(resized, (o) => true);
    }

    void resized(object obj)
    {
        var canvas = obj as Canvas;
        xCenter = canvas.ActualWidth / 2;
        yCenter = canvas.ActualHeight / 2;
        Series.Clear();
        DrawPie(array, brushes, radius);
    }

    void DrawPie(double[] values, Brush[] colors, double radius)
    {
        var sum = values.Sum();
        double startAngle, sweepAngle;
        startAngle = sweepAngle = 0;

        for (int i = 0; i < values.Length; i++)
        {
            startAngle += sweepAngle;
            sweepAngle = 2 * Math.PI * values[i] / sum;

            var line1 = new LineSegment() { Point = new Point(xCenter + radius * Math.Cos(startAngle), yCenter + radius * Math.Sin(startAngle)) };
            var line2 = new LineSegment() { Point = new Point(xCenter + radius * Math.Cos(startAngle + sweepAngle), yCenter + radius * Math.Sin(startAngle + sweepAngle)) };
            var arc = new ArcSegment()
            {
                SweepDirection = SweepDirection.Clockwise,
                Size = new Size(radius, radius),
                Point = new Point(xCenter + radius * Math.Cos(startAngle + sweepAngle), yCenter + radius * Math.Sin(startAngle + sweepAngle))
            };
            var figure = new PathFigure() { IsClosed = true, StartPoint = new Point(xCenter, yCenter), Segments = { line1, arc, line2 } };

            Series.Add(new ShapeData()
            {
                Geometry = new PathGeometry() { Figures = { figure } },
                Fill = colors[i],
                Stroke = Brushes.Red,
                StrokeThickness = 1
            });
        }
    }

    #region Notify Property Changed Members
    public event PropertyChangedEventHandler PropertyChanged;
    void OnPropertyChanged([CallerMemberName] string name = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

    #endregion
}

xaml中使用它,我在<ItemsControl Grid.Row="1" ItemsSource="{Binding Series}"> <ItemsControl.ItemTemplate> <DataTemplate> <Path Data="{Binding Geometry}" Fill="{Binding Fill}" Stroke="{Binding Stroke}" StrokeThickness="{Binding StrokeThickness}"/> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Canvas x:Name="panel"> <i:Interaction.Triggers> <i:EventTrigger EventName="SizeChanged"> <i:InvokeCommandAction Command="{Binding Resized}" CommandParameter="{Binding ElementName=panel}"/> </i:EventTrigger> <i:EventTrigger EventName="Loaded"> <i:InvokeCommandAction Command="{Binding Resized}" CommandParameter="{Binding ElementName=panel}"/> </i:EventTrigger> </i:Interaction.Triggers> </Canvas> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> 标签上收到警告

  

找不到类型的默认构造函数   'System.Windows.Interactivity.TriggerCollection'。您可以使用   用于构造此类型的参数或FactoryMethod指令。

0 个答案:

没有答案
相关问题