我需要绘制一条对角线,这样我们就可以展示一条中间线来表示某些东西的进展,这些东西可能不一定是(0,0),(1,1)....(4,4),它可能会根据X和Y轴之间的比例而出现偏差。
在我同意将该行绘制为LineSeries
后,我遇到了重复轴的问题。
问题:我需要绘制3行系列 第一名:金额 - >访问量 第二名:目标 - >月目标(这是X轴平行线) 第3名:中线 - >正常访问进度的中间线。
金额系列可能包括从第1天到第10天的天数,但是中线系列始终必须显示所有月份日期(3月份为1到31)。那么,我们现在有两个系列(目标除外)共享相同的X轴,而因为他们的数据不同,每个人都在创建自己的X轴(顶部和底部),我希望两个系列都采用相同的底部X轴(从第1天到第31天),当第10天到来时,数量系列线将停止被绘制但是中线将继续直到第31天。
这是我想要的图片:
看到黑线?这就是我想成为中间线的东西,但到目前为止,灰线是我能够做到的。
在这张图片中,我只有4个数据点,如果它们真的是31个数据点,中间线将被正确绘制,但我想正确绘制它而不管数据点数。为简单起见,在此示例中未显示重复的上X轴,因为我将相似的数据提供给两个系列,但是如果我将整个月数据分配给中间线,则它将创建一个上部1-31天的X轴。 / p>
答案 0 :(得分:1)
我得到的证据表明,第一个想法是最正确的。 如果你只想要一条黑线 - 解决方案非常简单:
LineSeries
以显示图表右侧的图例。以下是图表:
<charting:Chart Grid.Row="1">
<charting:Chart.Series>
<charting:LineSeries ItemsSource="{Binding LineItems}" IndependentValuePath="Date" DependentValuePath="Value" />
<charting:LineSeries Title="Middle Line">
<charting:LineSeries.DataPointStyle>
<Style TargetType="Control">
<Setter Property="Background" Value="Black"/>
</Style>
</charting:LineSeries.DataPointStyle>
</charting:LineSeries>
</charting:Chart.Series>
<charting:Chart.Template>
<ControlTemplate TargetType="charting:Chart">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<datavis:Title Content="{TemplateBinding Title}" Style="{TemplateBinding TitleStyle}" />
<Grid Grid.Row="1" Margin="0,15,0,15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<datavis:Legend x:Name="Legend" Header="{TemplateBinding LegendTitle}" Style="{TemplateBinding LegendStyle}" Grid.Column="1" />
<chartingprimitives:EdgePanel x:Name="ChartArea" Style="{TemplateBinding ChartAreaStyle}">
<Grid Canvas.ZIndex="-1" Style="{TemplateBinding PlotAreaStyle}" />
<Border Canvas.ZIndex="10" BorderBrush="#FF919191" BorderThickness="1" />
<local:LineControl Canvas.ZIndex="11"/>
</chartingprimitives:EdgePanel>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</charting:Chart.Template>
</charting:Chart>
它在控件模板中包含LineControl
,它是UserControl
,代码如下:
<强> LineControl.xaml 强>
<Line x:Name="line" Stroke="Black" StrokeThickness="2" />
<强> LineControl.xaml.cs 强>
public LineControl()
{
InitializeComponent();
this.SizeChanged += new SizeChangedEventHandler(LineControl_SizeChanged);
}
void LineControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
this.line.Y1 = this.ActualHeight;
this.line.X2 = this.ActualWidth;
}
此解决方案的优点:
另一种解决方案 - 使用行为。
图表XAML几乎没有变化:我添加了行为并删除了UserControl
和中间行LineSeries
<chartingprimitives:EdgePanel x:Name="ChartArea" Style="{TemplateBinding ChartAreaStyle}">
<i:Interaction.Behaviors>
<local:DiagonalLineBehavior/>
</i:Interaction.Behaviors>
行为类:
public class DiagonalLineBehavior : Behavior<EdgePanel>
{
private DateTimeAxis xAxis;
private LinearAxis yAxis;
public Brush LineColor { get; set; }
public DiagonalLineBehavior()
{
if (this.LineColor == null)
this.LineColor = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
}
void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
xAxis = this.AssociatedObject.Children.OfType<DateTimeAxis>().FirstOrDefault(ax => ax.Orientation == AxisOrientation.X);
yAxis = this.AssociatedObject.Children.OfType<LinearAxis>().FirstOrDefault(ax => ax.Orientation == AxisOrientation.Y);
if (xAxis == null || yAxis == null)
return;
this.UpdateLine();
}
private void UpdateLine()
{
//Collection with two items: start point and end point
var lineSource = new[] {
new ChartPointModel(xAxis.ActualMinimum, yAxis.ActualMinimum),
new ChartPointModel(xAxis.ActualMaximum, yAxis.ActualMaximum) };
//This code creates a line with many intermediate points
//var pointCount = (int)Math.Ceiling(((TimeSpan)(xAxis.ActualMaximum - xAxis.ActualMinimum)).TotalDays/xAxis.ActualInterval);
//var yInterval = (yAxis.ActualMaximum - yAxis.ActualMinimum).Value / (double)pointCount;
//var lineSource = Enumerable.Range(0, pointCount)
// .Select(i => new ChartPointModel(xAxis.ActualMinimum.Value.AddDays(xAxis.ActualInterval * i), yAxis.ActualMinimum + yInterval * i))
// .ToList();
var chart = GetParent<Chart>(this.AssociatedObject);
//Style with hidden markers and some color
var emptyDataPointStyle = new Style(typeof(DataPoint));
emptyDataPointStyle.Setters.Add(new Setter(DataPoint.OpacityProperty, 0));
emptyDataPointStyle.Setters.Add(new Setter(DataPoint.BackgroundProperty, this.LineColor));
//Line series
chart.Series.Insert(0, new LineSeries()
{
ItemsSource = lineSource,
Title = "Middle Line",
IndependentValuePath = "X",
DependentValuePath = "Y",
DataPointStyle = emptyDataPointStyle
});
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.Loaded -= AssociatedObject_Loaded;
}
private T GetParent<T>(DependencyObject d) where T : DependencyObject
{
if (d == null)
return null;
var parent = VisualTreeHelper.GetParent(d);
if (parent != null && parent is T)
return (T)parent;
else return GetParent<T>(parent);
}
public class ChartPointModel : DependencyObject
{
public ChartPointModel(DateTime? x, double? y)
{
this.X = x;
this.Y = y;
}
public DateTime? X { get; set; }
public double? Y { get; set; }
}
}
优势只有一个:该行是真实的,并且具有与DataSeries相同的属性。但它需要更多的代码,每边都有空格,与图表类型紧密相连,并且在第一次出现后不会改变。
所以尝试第一个解决方案,如果它不是你所期望的 - 我将尝试用第二个解决方案做点什么。