InteractiveDataDisplay.WPF使动态图表成为值记录器外观

时间:2017-11-29 13:06:52

标签: c# wpf wpf-controls customization dynamic-data-display

我使用Microsoft InteractiveDataDisplay.WPF(以前的DynamicDataDisplay)来显示实时数据(大约2-3秒)。 这段代码是xaml.cs:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            double[] y = new double[200];
            double[] x = new double[200];
            for (int i = 0; i < 200; i++)
            {
                y[i] = 3.1415 * i / (y.Length - 1);
                x[i] = DateTime.Now.AddMinutes(-i).ToOADate();
            }
            linegraph.Plot(x, y);
        }
    }

使用此xaml:

<d3:Chart Name="plotter">
            <d3:Chart.Title>
                <TextBlock HorizontalAlignment="Center" FontSize="18" Margin="0,5,0,5">chart sample</TextBlock>                
            </d3:Chart.Title>
            <d3:LineGraph x:Name="linegraph" Description="Simple linegraph" Stroke="Blue" StrokeThickness="3">
            </d3:LineGraph>
        </d3:Chart>

提供此观点: sample chart 但我想要关注自定义图表: enter image description here

任何想法怎么做?谢谢!

更新1 (使用Kevin Ross解决方案): enter image description here

更新2 (使用Dmitry Voytsekhovskiy解决方案): Time axis (Y) not move with data.

但是时间轴(Y)不同步而不随数据移动。如何解决这个问题?

3 个答案:

答案 0 :(得分:3)

如何将轴移到顶部?

图表布局模板在Themes/Generic.xaml中定义为d3:Chart的样式。

您可以创建自定义样式,其中水平轴位于顶部(d3:Figure.Placement="Top")并且方向正确(AxisOrientation="Top")。例如,

<d3:PlotAxis x:Name="PART_horizontalAxis"
         d3:Figure.Placement="Top" 
         AxisOrientation="Top"
         Foreground="{TemplateBinding Foreground}">
   <d3:MouseNavigation IsVerticalNavigationEnabled="False"/>
</d3:PlotAxis>

如何为轴标签使用自定义格式?

例如,如果沿y的值实际上是自特定时刻起的小时数,并且您希望将轴刻度显示为HH:mm,则需要将自定义label provider注入axis control

为此,您可以创建从Axis派生的新轴类,并将自定义标签提供程序传递给基础构造函数:

public class CustomLabelProvider : ILabelProvider
{
    public static DateTime Origin = new DateTime(2000, 1, 1);

    public FrameworkElement[] GetLabels(double[] ticks)
    {
        if (ticks == null)
            throw new ArgumentNullException("ticks");


        List<TextBlock> Labels = new List<TextBlock>();
        foreach (double tick in ticks)
        {
            TextBlock text = new TextBlock();
            var time = Origin + TimeSpan.FromHours(tick);
            text.Text = time.ToShortTimeString();
            Labels.Add(text);
        }
        return Labels.ToArray();
    }
}

public class CustomAxis : Axis
{
    public CustomAxis() : base(new CustomLabelProvider(), new TicksProvider())
    {
    }
}

现在返回自定义图表模板,并将垂直轴的类型从PlotAxis更改为CustomAxis(请注意,您可能需要更改类型前缀):

<d3:CustomAxis x:Name="PART_verticalAxis"
             d3:Figure.Placement="Left" 
             AxisOrientation="Left"
             Foreground="{TemplateBinding Foreground}">
    <d3:MouseNavigation IsHorizontalNavigationEnabled="False"/>
</d3:CustomAxis>

如果我们为LineGraphSample执行上述步骤并运行它,我们会得到以下结果:

Custom labels for vertical axis and horizontal axis at top

最后,自定义图表样式:

<Style TargetType="d3:Chart">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="d3:Chart">
                <Grid>
                    <d3:Figure x:Name="PART_figure" Margin="1"
                               PlotHeight="{Binding PlotHeight, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               PlotWidth="{Binding PlotWidth, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               PlotOriginX="{Binding PlotOriginX, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               PlotOriginY="{Binding PlotOriginY, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               IsAutoFitEnabled="{Binding IsAutoFitEnabled, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               AspectRatio="{Binding AspectRatio, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               ExtraPadding="{TemplateBinding BorderThickness}"
                               Background="{TemplateBinding Background}">
                        <d3:MouseNavigation IsVerticalNavigationEnabled="{TemplateBinding IsVerticalNavigationEnabled}"
                                            IsHorizontalNavigationEnabled="{TemplateBinding IsHorizontalNavigationEnabled}"
                                            x:Name="PART_mouseNavigation"/>
                        <d3:KeyboardNavigation IsVerticalNavigationEnabled="{TemplateBinding IsVerticalNavigationEnabled}"
                                               IsHorizontalNavigationEnabled="{TemplateBinding IsHorizontalNavigationEnabled}"
                                               x:Name="PART_keyboardNavigation"/>
                        <d3:VerticalContentControl d3:Figure.Placement="Left"
                                                   Content="{TemplateBinding LeftTitle}"
                                                   VerticalAlignment="Center"
                                                   IsTabStop="False"/>
                            <d3:CustomAxis x:Name="PART_verticalAxis"
                                     d3:Figure.Placement="Left" 
                                     AxisOrientation="Left"
                                     Foreground="{TemplateBinding Foreground}">
                            <d3:MouseNavigation IsHorizontalNavigationEnabled="False"/>
                        </d3:CustomAxis>
                        <d3:AxisGrid x:Name="PART_axisGrid"
                                     VerticalTicks="{Binding Ticks,ElementName=PART_verticalAxis, Mode=OneWay}"
                                     HorizontalTicks="{Binding Ticks,ElementName=PART_horizontalAxis, Mode=OneWay}"
                                     Stroke="{TemplateBinding Foreground}" Opacity="0.25"/>
                        <ContentControl d3:Figure.Placement="Top" 
                                        HorizontalAlignment="Center"
                                        FontSize="16"
                                        Content="{TemplateBinding Title}"
                                        Foreground="{TemplateBinding Foreground}"
                                        IsTabStop="False"/>
                        <ContentControl d3:Figure.Placement="Bottom" 
                                        HorizontalAlignment="Center"
                                        Content="{TemplateBinding BottomTitle}"
                                        Foreground="{TemplateBinding Foreground}"
                                        IsTabStop="False"/>
                        <d3:VerticalContentControl d3:Figure.Placement="Right"
                                                   Content="{TemplateBinding RightTitle}"
                                                   VerticalAlignment="Center"
                                                   IsTabStop="False"/>
                        <d3:PlotAxis x:Name="PART_horizontalAxis"
                                     d3:Figure.Placement="Top" 
                                     AxisOrientation="Top"
                                     Foreground="{TemplateBinding Foreground}">
                            <d3:MouseNavigation IsVerticalNavigationEnabled="False"/>
                        </d3:PlotAxis>
                        <ContentPresenter/>
                        <Border BorderThickness="{TemplateBinding BorderThickness}"
                                BorderBrush="{TemplateBinding Foreground}" d3:Figure.Placement="Center"/>
                        <d3:Legend x:Name="PART_legend" 
                                   Foreground="Black" Content="{TemplateBinding LegendContent}"
                                   Visibility="{TemplateBinding LegendVisibility}"/>
                    </d3:Figure>
                    <Rectangle x:Name="FocusVisualElement" RadiusX="2" RadiusY="2" Stroke="#FF6DBDD1" StrokeThickness="1" Opacity="0" IsHitTestVisible="false" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="IsTabStop" Value="False"/>
</Style>

答案 1 :(得分:1)

这就是我想出来的,它的边缘非常粗糙但是应该帮助你。您的视图XAML基本保持不变我刚刚添加了一个启动和停止按钮的按钮

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <Button Click="Button_Click" Content="GO"/>

    <d3:Chart Name="plotter" Grid.Row="1">
        <d3:Chart.Title>
            <TextBlock HorizontalAlignment="Center" FontSize="18" Margin="0,5,0,5">chart sample</TextBlock>
        </d3:Chart.Title>
        <d3:LineGraph x:Name="linegraph" Description="Simple linegraph" Stroke="Blue" StrokeThickness="3">

        </d3:LineGraph>
    </d3:Chart>
</Grid>

您背后的代码变为

public partial class LiveView : Window
{
    private const int DataPointsToShow = 100;
    public Tuple<LinkedList<double>, LinkedList<double>> GraphData = new Tuple<LinkedList<double>, LinkedList<double>>(new LinkedList<double>(), new LinkedList<double>());
    public Timer GraphDataTimer;

    public LiveView()
    {
        InitializeComponent();
        GraphDataTimer = new Timer(50);
        GraphDataTimer.Elapsed += GraphDataTimer_Elapsed;
    }

    private void GraphDataTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        Random random = new Random();
        if (GraphData.Item1.Count() > DataPointsToShow)
        {
            GraphData.Item1.RemoveFirst();
            GraphData.Item2.RemoveFirst();
        }

        GraphData.Item1.AddLast(random.NextDouble()*200);
        GraphData.Item2.AddLast(DateTime.Now.ToOADate());
        Dispatcher.Invoke(() =>
        {
            linegraph.Plot(GraphData.Item1, GraphData.Item2);
        });

    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if (GraphDataTimer.Enabled)
        {
            GraphDataTimer.Stop();
        }
        else
        {
            GraphDataTimer.Start();
        }
    }
}

基本上它的作用是每50毫秒产生一个新值并将其添加到链表的末尾。如果总点数高于您想要显示的数字,那么它也会删除第一个点,从而为您提供一个不断滚动的图表,其中最新的数据位于顶部。

答案 2 :(得分:1)

我设法改进了德米特里(Dmitry)建议的解决方案,以使轴保持与图形的链接。

<Style x:Key="timeAxisStyle" TargetType="d3:PlotAxis">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="d3:PlotAxis">
                    <Grid>
                        <local:CustomAxis x:Name="PART_Axis" 
                             AxisOrientation="{Binding AxisOrientation, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                             IsReversed="{Binding IsReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                             Ticks="{Binding Ticks, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                             Foreground="{Binding Foreground, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/>
                        <ContentPresenter/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="IsTabStop" Value="False"/>
    </Style>
    <Style TargetType="d3:Chart">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="d3:Chart">
                    <Grid>
                        <d3:Figure x:Name="PART_figure" Margin="1"
                               PlotHeight="{Binding PlotHeight, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               PlotWidth="{Binding PlotWidth, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               PlotOriginX="{Binding PlotOriginX, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               PlotOriginY="{Binding PlotOriginY, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               IsXAxisReversed = "{Binding IsXAxisReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               IsYAxisReversed = "{Binding IsXAxisReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               IsAutoFitEnabled="{Binding IsAutoFitEnabled, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               AspectRatio="{Binding AspectRatio, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                               ExtraPadding="{TemplateBinding BorderThickness}"
                               Background="{TemplateBinding Background}">
                            <d3:MouseNavigation IsVerticalNavigationEnabled="{TemplateBinding IsVerticalNavigationEnabled}"
                                            IsHorizontalNavigationEnabled="{TemplateBinding IsHorizontalNavigationEnabled}"
                                            x:Name="PART_mouseNavigation"/>
                            <d3:KeyboardNavigation IsVerticalNavigationEnabled="{TemplateBinding IsVerticalNavigationEnabled}"
                                               IsHorizontalNavigationEnabled="{TemplateBinding IsHorizontalNavigationEnabled}"
                                               x:Name="PART_keyboardNavigation"/>
                            <d3:VerticalContentControl d3:Figure.Placement="Left"
                                                   Content="{TemplateBinding LeftTitle}"
                                                   VerticalAlignment="Center"
                                                   IsTabStop="False"/>
                            <d3:PlotAxis x:Name="PART_verticalAxis"
                                     d3:Figure.Placement="Left" 
                                     AxisOrientation="Left"
                                     IsReversed = "{Binding IsYAxisReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                                     Foreground="{TemplateBinding Foreground}"
                                     Style="{StaticResource timeAxisStyle}">
                                <d3:MouseNavigation IsHorizontalNavigationEnabled="False"/>
                            </d3:PlotAxis>
                            <d3:AxisGrid x:Name="PART_axisGrid"
                                     VerticalTicks="{Binding Ticks,ElementName=PART_verticalAxis, Mode=OneWay}"
                                     HorizontalTicks="{Binding Ticks,ElementName=PART_horizontalAxis, Mode=OneWay}"
                                     IsXAxisReversed = "{Binding IsXAxisReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                                     IsYAxisReversed = "{Binding IsXAxisReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                                     Stroke="{TemplateBinding Foreground}" Opacity="0.25"/>
                            <ContentControl d3:Figure.Placement="Top" 
                                        HorizontalAlignment="Center"
                                        FontSize="16"
                                        Content="{TemplateBinding Title}"
                                        Foreground="{TemplateBinding Foreground}"
                                        IsTabStop="False"/>
                            <ContentControl d3:Figure.Placement="Bottom" 
                                        HorizontalAlignment="Center"
                                        Content="{TemplateBinding BottomTitle}"
                                        Foreground="{TemplateBinding Foreground}"
                                        IsTabStop="False"/>
                            <d3:VerticalContentControl d3:Figure.Placement="Right"
                                                   Content="{TemplateBinding RightTitle}"
                                                   VerticalAlignment="Center"
                                                   IsTabStop="False"/>
                            <d3:PlotAxis x:Name="PART_horizontalAxis"
                                     d3:Figure.Placement="Bottom" 
                                     AxisOrientation="Bottom"
                                     IsReversed = "{Binding IsXAxisReversed, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                                     Foreground="{TemplateBinding Foreground}">
                                <d3:MouseNavigation IsVerticalNavigationEnabled="False"/>
                            </d3:PlotAxis>
                            <ContentPresenter/>
                            <Border BorderThickness="{TemplateBinding BorderThickness}"
                                BorderBrush="{TemplateBinding Foreground}" d3:Figure.Placement="Center"/>
                            <d3:Legend x:Name="PART_legend" 
                                   Foreground="Black" Content="{TemplateBinding LegendContent}"
                                   Visibility="{TemplateBinding LegendVisibility}"/>
                        </d3:Figure>
                        <Rectangle x:Name="FocusVisualElement" RadiusX="2" RadiusY="2" Stroke="#FF6DBDD1" StrokeThickness="1" Opacity="0" IsHitTestVisible="false" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="IsTabStop" Value="False"/>
    </Style>