没有第三方库,WPF有没有首选的方法来进行二维图表处理?

时间:2016-11-03 15:32:06

标签: c# wpf mvvm charts

我想做一个项目,为图表制作一个非常通用的可重用用户控件,这种控件几乎没有形状。我希望能够在图表中弄湿自己,但似乎地球上几乎每个人都使用WPF工具包或麻雀图表来免费制作第三方制作图表。有没有人有绑定或构建完全孤立的图表制作方法的经验?我正在考虑做一些通用的事情,就像在画布中绑定折线并传递一样。我很好奇是否有其他人在这条路上,并提出了设置事件提升和潜在依赖属性的绑定的技巧。我正在考虑遵循MVVM架构方法并完成对ViewModel的大部分绑定,但我最终希望公开要更新的属性。

与概念类似(UserControl嵌入另一个View或MainForm):

<StackPanel>
    <Label x:Name="lblCustomDataGridHeader" Content="{Binding TestText}"  HorizontalAlignment="Center" FontSize="24"/>
    <Canvas Height="260" Width="300">
      <Polyline Points="{Binding Points}" Stroke="LightBlue" StrokeThickness="4" />
    </Canvas>
  </StackPanel>

ViewModel属性:

public ViewModel()
{
    TestText = "Line Chart";
    //Obviously some converter or something else here to accept one or many   lines
    Points = "0,260 10,250 20,245 40,200 50,250 80, 200, 140,100";
}

public string TestText {
    get { return _testText; }
    set 
    {
        _testText = value;
        OnPropertyChanged(NameOf(TestText));
    }
}

private string _points;
public string Points {
    get { return _points; }
    set 
    {
        _points = value;
        OnPropertyChanged(NameOf(Points));
    }
}

稍后编辑

我还尝试过一个绑定到类

的模板化控件
 <Style TargetType="{x:Type local:LineGraph}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:LineGraph}">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
          </Grid.RowDefinitions>
          <TextBlock Grid.Row="0" Text="Hello" FontSize="20"/>
          <Border Grid.Row="1" BorderThickness="1" BorderBrush="Black" CornerRadius="15" Margin="10">
            <Canvas Margin="10" x:Name="PART_Canvas">
              <Canvas.LayoutTransform>
                <ScaleTransform ScaleX="1" ScaleY="-1" />
              </Canvas.LayoutTransform>
            </Canvas>
          </Border>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

类(其中一些需要清理,因为我使用的是其他人的实现,它是在VB.NET中并转换过来的):

public class LineGraph : Control, INotifyPropertyChanged
{

    //CONSTRUCTOR
    static LineGraph()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(LineGraph), new FrameworkPropertyMetadata(typeof(LineGraph)));
    }

    public event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;

    public void OnPropertyChanged(string info)
    {
        if (PropertyChanged != null) {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }


    public static readonly DependencyProperty _Trends = DependencyProperty.RegisterReadOnly("Trends", typeof(Collection<ChartDataSegment>), typeof(LineGraph), new PropertyMetadata(new Collection<ChartDataSegment>())).DependencyProperty;
    public Collection<ChartDataSegment> Trends {
        get { return (Collection<ChartDataSegment>)GetValue(_Trends); }
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        dynamic canvas = GetTemplateChild("PART_Canvas") as Canvas;
        if (canvas != null && Trends != null) {
            foreach (void trend_loopVariable in Trends) {
                trend = trend_loopVariable;
                DrawTrend(canvas, trend);
            }
        }
    }

    private void DrawTrend(Canvas drawingCanvas, ChartDataSegment Trend)
    {
        dynamic t = Trend as ChartDataSegment;
        if (t != null && t.Points != null) {
            for (int i = 1; i <= t.Points.Count - 1; i++) {
                dynamic toDraw = new Line {
                    X1 = t.Points(i - 1).X,
                    Y1 = t.Points(i - 1).Y,
                    X2 = t.Points(i).X,
                    Y2 = t.Points(i).Y,
                    StrokeThickness = 2,
                    Stroke = t.LineColor
                };
                drawingCanvas.Children.Add(toDraw);
            }
        }
    }

}

public class ChartDataSegment : DependencyObject
{


    public static readonly DependencyProperty _LineColor = DependencyProperty.Register("LineColor", typeof(Brush), typeof(ChartDataSegment), new PropertyMetadata(null));
    public Brush LineColor {
        get { return (Brush)GetValue(_LineColor); }
        set { SetValue(_LineColor, value); }
    }

    public static readonly DependencyProperty _LineThickness = DependencyProperty.Register("LineThickness", typeof(Thickness), typeof(ChartDataSegment), new PropertyMetadata(null));
    public Thickness PointThickness {
        get { return (Thickness)GetValue(_LineThickness); }
        set { SetValue(_LineThickness, value); }
    }

    public static readonly DependencyProperty _Points = DependencyProperty.Register("Points", typeof(ObservableCollection<Point>), typeof(ChartDataSegment), new UIPropertyMetadata(null));
    public ObservableCollection<Point> Points {
        get { return (ObservableCollection<Point>)GetValue(_Points); }
        set { SetValue(_Points, value); }
    }
}

在ViewModel中实现:

var lineTrend1 = new ChartDataSegment {
    LineColor = Brushes.Blue,
    Points = new ObservableCollection<Point>({
        new Point {
            X = 1,
            Y = 1
        },
        new Point {
            X = 50,
            Y = 20
        },
        new Point {
            X = 100,
            Y = 100
        },
        new Point {
            X = 150,
            Y = 130
        }
    })
};

var ctrl = new LineGraph();
ctrl.Trends.Add(lineTrend1);

我最关心的不是这可以实现,而是按需要注入项目,不仅仅是在实例化,而是在对象已经运行后继续按需更新行。 EG:异步调用更新折线图而不是托管需要处理然后调用的静态图表。

我的直接问题是Stack Overflow想要解决问题的具体细节是:“你能轻松地注入集合(点)的集合(行)并使用具有依赖属性的Canvas进行自我更新吗?” < / p>

3 个答案:

答案 0 :(得分:3)

我制作了一个我需要的特定类型的图表,就像你一样,我没有使用任何第三方库。我正在寻找它,我找到了这个项目项目: http://www.codeproject.com/Articles/446888/Custom-Spider-or-Radar-Chart-Control

他们制作了类似于以下内容的图表:

enter image description here

这些来源可供下载,并且不使用第三方代码,因此您可以轻松查看所有内容。

要创建图表,只需执行以下操作:

<WpfCharts:SpiderChart Title="Spider chart"  
                   Lines="{Binding Lines}" 
                   Axis="{Binding Axes}" 
                   Minimum="0" 
                   Maximum="1" 
                   Ticks="5" 
                   ShowLegend="True" 
                   LegendLocation="BottomRight" 
                   LegendBackgroundColor="Aquamarine"/> 

你可以看到它已经绑定了属性。我已经在MVVM项目中使用它了。

我希望它能引导你找到你想要的东西。

答案 1 :(得分:0)

是的,你可以创建一个图表控件,一个简单的图表&#34; Chart&#34;,当你想要扩展那段代码时真正的工作开始,那么你必须记住在某些时候你的要求可以改变,你开始的设计将不再好。我个人认为这是一个很好的练习。但是你需要记住,你将收到的数据会发生变化,没有人会希望传递Point的集合,我个人想要传递整数或浮点数或小数的集合。几乎无论我喜欢什么。此时,您将开始思考另一种再次实现所有逻辑的方法。这几个月后我的朋友就是这样。我并不是想阻止你接受这项任务,但你的起点必将在不久的将来改变 听听Will他知道他在说什么。

答案 2 :(得分:-1)

我使用并建议使用WPF Toolkit在WPF中使用MVVM模式绘制图表。

在视图中:

<chartingToolkit:Chart>
    <chartingToolkit:Chart.Axes>
        <chartingToolkit:LinearAxis Orientation="Y" SeriesHost="{x:Null}" Minimum="0"/>
    </chartingToolkit:Chart.Axes>
    <chartingToolkit:ColumnSeries DependentValueBinding="{Binding Value, Mode=OneWay}" IndependentValueBinding="{Binding Key, Mode=OneWay}" ItemsSource="{Binding ChartData}"/>
</chartingToolkit:Chart>

在ViewModel中:

public ObservableCollection<KeyValuePair<string,int> ChartData;

private void DrawChart()
{
    //do not forget initialize ChartData in constructor
    ChartData.Add(new KeyValuePair<string,int>("type1",10));
    ChartData.Add(new KeyValuePair<string,int>("type2",20));
    ChartData.Add(new KeyValuePair<string,int>("type3",30));
}