在Silverlight图表中绘制对角线

时间:2011-03-05 11:04:12

标签: silverlight charts

我需要绘制一条对角线,这样我们就可以展示一条中间线来表示某些东西的进展,这些东西可能不一定是(0,0),(1,1)....(4,4),它可能会根据X和Y轴之间的比例而出现偏差。

在我同意将该行绘制为LineSeries后,我遇到了重复轴的问题。

问题:我需要绘制3行系列 第一名:金额 - >访问量 第二名:目标 - >月目标(这是X轴平行线) 第3名:中线 - >正常访问进度的中间线。

金额系列可能包括从第1天到第10天的天数,但是中线系列始终必须显示所有月份日期(3月份为1到31)。那么,我们现在有两个系列(目标除外)共享相同的X轴,而因为他们的数据不同,每个人都在创建自己的X轴(顶部和底部),我希望两个系列都采用相同的底部X轴(从第1天到第31天),当第10天到来时,数量系列线将停止被绘制但是中线将继续直到第31天。

这是我想要的图片:
Middle line series

看到黑线?这就是我想成为中间线的东西,但到目前为止,灰线是我能够做到的。

在这张图片中,我只有4个数据点,如果它们真的是31个数据点,中间线将被正确绘制,但我想正确绘制它而不管数据点数。为简单起见,在此示例中未显示重复的上X轴,因为我将相似的数据提供给两个系列,但是如果我将整个月数据分配给中间线,则它将创建一个上部1-31天的X轴。 / p>

1 个答案:

答案 0 :(得分:1)

我得到的证据表明,第一个想法是最正确的。 如果你只想要一条黑线 - 解决方案非常简单:

  1. 更改图表的控件模板
  2. 添加虚拟LineSeries以显示图表右侧的图例。
  3. 以下是图表:

       <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相同的属性。但它需要更多的代码,每边都有空格,与图表类型紧密相连,并且在第一次出现后不会改变。

    所以尝试第一个解决方案,如果它不是你所期望的 - 我将尝试用第二个解决方案做点什么。