Рисование диагональной линии в Silverlight Chart - PullRequest
2 голосов
/ 05 марта 2011

Мне нужно нарисовать диагональную линию, чтобы мы могли продемонстрировать среднюю линию для прогресса чего-то, что может не обязательно идти (0,0), (1,1) .... (4, 4), это может отклоняться в зависимости от соотношения между осями X и Y.

После того, как я согласился нарисовать линию как LineSeries, у меня возникла проблема дублирования осей.

Проблема: Мне нужно нарисовать 3 ряда линий.
1й: Сумма -> Количество посещений.
2nd: Target -> месячная цель (это параллельная линия оси X).
3-й: Средняя линия -> Средняя линия нормального прогресса посещений.

Серия Amount может включать дни с 1-го по 10-й день, но в серии Midline всегда необходимо указывать все месячные дни (для марта это от 1 до 31). Итак, у нас есть 2 серии сейчас (кроме цели) , которые имеют одинаковую ось X , и , поскольку их данные различны, каждый создает свою ось X (вверху и внизу), я хочу, чтобы обе серии проходили по одной и той же нижней оси X (с 1-го по 31-й день), и когда наступит 10-й день, линия серии сумм прекратит рисоваться, но средняя линия продолжится до 31-го дня

Вот картинка того, что я хочу:
Middle line series

Видите черную линию? это то, что я хочу быть средней линией, но вместо этого серая линия - это то, что я смог сделать.

На этом рисунке У меня есть только 4 точки данных, если бы они действительно были 31 точкой данных, средняя линия будет нарисована правильно, но я хочу нарисовать ее правильно, независимо от количества точек данных. для простоты дублирующая верхняя ось X в этом примере не показана, потому что я дал одинаковые данные для обеих серий, но если я назначил данные за весь месяц средней линии, это создаст верхнюю ось X в течение 1-31 дней.

1 Ответ

1 голос
/ 08 марта 2011

Я получил дополнительные доказательства того, что первая идея является наиболее правильной.Если вам нужна просто черная линия - решение довольно простое:

  1. Измените шаблон элемента управления диаграммы
  2. Добавьте манекен LineSeries, чтобы отобразить Легенду справа отchart.

Вот диаграмма:

   <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.Но для этого требуется гораздо больше кода, с каждой стороны имеются пустые места, тесно связанные с типом диаграммы, и он не изменяется после первого появления.

Так что попробуйте первое решение, а если нетчто вы ожидаете - я попытаюсь что-то сделать со вторым решением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...