WPF 图表工具包:如何​​在添加实时数据时设置固定的 X 轴范围

WPF Charting Toolkit: How to set a fixed X-axis range while adding real time data

我正在做一个基于 WPF 图表工具包的应用程序实时数据图表。我通过串行端口获取数据。 设置图表代码如下:

    <chartingToolkit:Chart  Margin="10,10,10,0" ClipToBounds="True" x:Name="chart1" Title="Chart Title">
        <chartingToolkit:LineSeries IndependentValueBinding="{Binding Value1}" DependentValueBinding="{Binding Value2}" ItemsSource="{Binding}" Background="Transparent" Cursor="No">
            <chartingToolkit:LineSeries.DataPointStyle>
                <Style TargetType="{x:Type chartingToolkit:LineDataPoint}">
                    <Setter Property="Height" Value="0"/>
                    <Setter Property="Width" Value="0" />
                    <Setter Property="Background" Value="Green"/>
                </Style>
            </chartingToolkit:LineSeries.DataPointStyle>
        </chartingToolkit:LineSeries>
    </chartingToolkit:Chart>

效果很好,但我仍然需要设置 X 轴的最大值和最小值。 X 值 (Value1) 是接收样本的数量,Y 轴值 (Value2) 显然是接收样本的具体值。

我的问题是关于X轴范围。

目前,我得到的最小值为 0,最大值为串行端口在当前时刻接收到的最大样本数。

但我想设置一个我想看到的永久 X 轴范围。

例如我想在 X 轴上看到 500 个样本的范围。

表示当样本数超过500时,max为最大样本数,min为max-500.

主要难点是在WPF中如何用实时数据设置??

谁能帮帮我吗??

更新问题

我正在根据@jstreet 的建议更新我的问题。

我有这个方法 运行 在 MainWindow class 的单独线程中,如下所示。

 public partial class MainWindow : Window 
  {
 public SerialPort serialPort1 = new SerialPort();
    public string rx_str = "";
    public string rx_str_copy;
    public int a;
    public double x, y;


      ObservableCollection<ChartData> chartData;
    ChartData objChartData;
    Thread myThread;

     public MainWindow()
    {
        InitializeComponent();
        string[] port = SerialPort.GetPortNames();
        foreach (string a in port)
        {
            comboPorts.Items.Add(a);
        }
        Array.Sort(port);
        comboPorts.Text = port[0];

        objChartData = new ChartData();
        chartData.Add(objChartData);
        chart1.DataContext = chartData;
        myThread = new Thread(new ThreadStart(Run));


    }

   public void Run()
       {
        while (true)
        {
            serialPort1.Write("a");
            rx_str = serialPort1.ReadTo("b");
            rx_str_copy = rx_str;
            x = a;
            y = Double.Parse(rx_str_copy,     CultureInfo.InvariantCulture);                                               

            a++;

            Dispatcher.Invoke(new Action(delegate
            {

         chartData.Add(new ChartData() { Value1 = x,         
       Value2= y             });          
            }));



        }
    }  

此运行() 方法负责接收数据并将其添加到图表。

在另一个 class 中,我对即将到来的数据和设置属性 Valeu1 和 Value2 有反应:

  public class ChartData : INotifyPropertyChanged
   {
    double _Value1;
    double _Value2;


    public double Value1
    {
        get
        {
            return _Value1;
        }
        set
        {
            _Value1 = value;
            OnPropertyChanged("Value1");
        }
    }

    public double Value2
    {
        get
        {
            return _Value2;
        }
        set
        {
            _Value2 = value;
            OnPropertyChanged("Value2");
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;

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

如何使@jstreet 的解决方案适应我的后台代码示例?

在您的视图模型中创建一个 MinValue 依赖项 属性 并将其绑定到您的轴 Minimum 属性。看看:

XAML:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp31"
        xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit" 
        x:Class="WpfApp31.MainWindow"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MyViewModel/>
    </Window.DataContext>
    <Grid>
        <chartingToolkit:Chart  Title="My Sample">
            <chartingToolkit:Chart.Axes>
                <chartingToolkit:LinearAxis Minimum="{Binding MinValue}" Orientation="X"></chartingToolkit:LinearAxis>
            </chartingToolkit:Chart.Axes>
            <chartingToolkit:LineSeries IndependentValueBinding="{Binding Value1}" 
                                        DependentValueBinding="{Binding Value2}" 
                                        ItemsSource="{Binding Data}">
            </chartingToolkit:LineSeries>
        </chartingToolkit:Chart>
    </Grid>
</Window>

查看模型:

public class MyViewModel : DependencyObject
{
    public int MinValue
    {
        get { return (int)GetValue(MinValueProperty); }
        set { SetValue(MinValueProperty, value); }
    }

    // Using a DependencyProperty as the backing store for MinValue.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MinValueProperty =
        DependencyProperty.Register("MinValue", typeof(int), typeof(MyViewModel), new PropertyMetadata(default(int)));

    public ObservableCollection<MyDataModel> Data { get; set; }

    private Timer serialPort;
    private Random y;
    private int x;
    private int range;

    public MyViewModel()
    {
        range = 10;
        Data = new ObservableCollection<MyDataModel>();
        y = new Random(DateTime.Now.Millisecond);
        serialPort = new Timer(DataReceived, null, 500, 500);
    }
    private void DataReceived(object state)
    {
        Application.Current.Dispatcher.Invoke(() => {
            Data.Add(new MyDataModel { Value1 = x, Value2 = y.Next(10, 90) });
            MinValue = x < range ? 0 : x - range;
            x++;
        });
    }
}

编辑: 作为记录,我可能不会像下面那样编写这段代码。我在这里这样做只是为了让你可以继续前进。

XAML:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApp1"
    xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit" 
    x:Class="WpfApp1.MainWindow"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="10*"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>

    <chartingToolkit:Chart Grid.Row="0"  Margin="10,10,10,0" ClipToBounds="True" x:Name="chart1" Title="Chart Title">
        <chartingToolkit:Chart.Axes>
            <chartingToolkit:LinearAxis Minimum="{Binding MinValue}" Orientation="X"></chartingToolkit:LinearAxis>
        </chartingToolkit:Chart.Axes>
        <chartingToolkit:LineSeries IndependentValueBinding="{Binding Value1}" DependentValueBinding="{Binding Value2}" ItemsSource="{Binding chartData}" Background="Transparent" Cursor="No">
            <chartingToolkit:LineSeries.DataPointStyle>
                <Style TargetType="{x:Type chartingToolkit:LineDataPoint}">
                    <Setter Property="Height" Value="0"/>
                    <Setter Property="Width" Value="0" />
                    <Setter Property="Background" Value="Green"/>
                </Style>
            </chartingToolkit:LineSeries.DataPointStyle>
        </chartingToolkit:LineSeries>
    </chartingToolkit:Chart>

    <Button Grid.Row="1" x:Name="btn1" Click="btn1_Click">START</Button>

</Grid>

CS:

public partial class MainWindow : Window
{
    public double MinValue
    {
        get { return (double)GetValue(MinValueProperty); }
        set { SetValue(MinValueProperty, value); }
    }

    // Using a DependencyProperty as the backing store for MinValue.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MinValueProperty =
        DependencyProperty.Register("MinValue", typeof(double), typeof(MainWindow), new PropertyMetadata(default(double)));

    //public SerialPort serialPort1 = new SerialPort();
    //public string rx_str = "";
    //public string rx_str_copy;
    //public int a;
    public double x, y;

    public ObservableCollection<ChartData> chartData { get; set; }
    ChartData objChartData;
    Thread myThread;
    Random r;
    int range = 50;

    public MainWindow()
    {
        InitializeComponent();

        r = new Random();

        DataContext = this;

        /*
        string[] port = SerialPort.GetPortNames();

        foreach (string a in port)
        {
            comboPorts.Items.Add(a);
        }

        Array.Sort(port);
        comboPorts.Text = port[0];
        */

        objChartData = new ChartData();
        chartData = new ObservableCollection<ChartData>();
        chartData.Add(objChartData);
        //chart1.DataContext = chartData;
        myThread = new Thread(new ThreadStart(Run));
    }

    private void btn1_Click(object sender, RoutedEventArgs e)
    {
        myThread.Start();
    }

    public void Run()
    {
        while (true)
        {
            //serialPort1.Write("a");
            //rx_str = serialPort1.ReadTo("b");
            //rx_str_copy = rx_str;

            //x = a;
            //y = Double.Parse(rx_str_copy, CultureInfo.InvariantCulture);

            //a++;

            Dispatcher.Invoke(new Action(delegate
            {
                chartData.Add(new ChartData()
                {
                    Value1 = x,
                    Value2 = r.NextDouble(),
                    //Value2 = y
                });
                MinValue = x < range ? 0 : x - range;
                x++;
            }));

            Thread.Sleep(50);
        }
    }
}