我使用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>
任何想法怎么做?谢谢!
更新2 (使用Dmitry Voytsekhovskiy解决方案):
但是时间轴(Y)不同步而不随数据移动。如何解决这个问题?
答案 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执行上述步骤并运行它,我们会得到以下结果:
最后,自定义图表样式:
<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>