在看了几个创建 piphart 的例子之后,我做了以下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
方法。我必须设置Canvas
的Width
和Height
,否则MainWindow
中不会显示任何内容。下面是我在MainWindow
中使用它的方式:
<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>
在 ViewModel 的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
回调中,我总是得到0
作为ActualHeight
和ActualWidth
的Canvas
,命名为chartArea
,所以当我调整窗口大小时,馅饼不会自动调整大小!
如何解决这个问题?
有没有更好的方法来绘制简单的饼图?在我的情况下List<PieSeries>
可能有 1 项到 5 项。
使用注释中建议的方法,它更简单!在我的 ViewModel,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
}
and inxaml
:
<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>
在xaml
中,我在<ItemsPanelTemplate>
标签上收到警告
未找到类型“System.Windows.Interactivity.TriggerCollection”的默认构造函数。可以使用 Arguments 或 FactoryMethod 指令构造此类型。
本站系公益性非盈利分享网址,本文来自用户投稿,不代表边看边学立场,如若转载,请注明出处
评论列表(61条)