LiveCharts 设置 DateTime X 轴 MinValue 和 MaxValue 以便缩放 in/out
LiveCharts set DateTime X-Axis MinValue and MaxValue in order to zoom in/out
我有一个使用 LiveCharts
的 WPF
应用程序,我在其中绘制了一个 LineGraph
,X 轴为 DateTime
。我的最终目标是实现 'two-way' 缩放。也就是说,我的应用程序上有两个 DateTimePicker
控件(来自 WPF Toolkit),它们代表 当前显示区域 的最小值和最大值 DateTime
图形,如果我在图形区域上使用滚轮缩放 in/out,更新的范围应该反映在所述控件上,并且(这是我正在努力的部分)相反,如果我设置 min/max 在 DateTimePicker
控件上,图形应相应地缩放 in/out。
我的XAML
很简单:
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<lvc:CartesianChart Name="MyChart"
Series="{Binding SeriesCollection}"
Zoom="X">
<lvc:CartesianChart.AxisX>
<lvc:Axis LabelFormatter="{Binding Formatter}"
PreviewRangeChangedCommand="{Binding XRangeChangedCommand}"
MinValue="{Binding TimeStampMin, Mode=TwoWay}"
MaxValue="{Binding TimeStampMax, Mode=TwoWay}"/>
</lvc:CartesianChart.AxisX>
</lvc:CartesianChart>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
<xceed:DateTimePicker Margin="4" Width="160" Name="dtpMinX"
Format="Custom" FormatString="yyyy/MM/dd HH:mm:ss"
Value="{Binding TimeStampMin, Mode=TwoWay}"/>
<xceed:DateTimePicker Margin="4" Width="160" Name="dtpMaxX"
Format="Custom" FormatString="yyyy/MM/dd HH:mm:ss"
Value="{Binding TimeStampMax, Mode=TwoWay}"/>
</StackPanel>
</Grid>
这是我的 DataPoint
class:
public class DataPoint
{
public DataPoint() { }
public DataPoint(DateTime timeStamp, double value)
{
TimeStamp = timeStamp;
Value = value;
}
public double Value { get; set; }
public DateTime TimeStamp { get; set; }
}
这是我的 PlotGraph()
方法,可以完成所有绘图工作。如果您想重现此应用程序,我会将我的整个 MainWindow()
代码包含在此 post 的底部。
private void PlotGraph()
{
var mapper = Mappers.Xy<DataPoint>()
.X(dp => dp.TimeStamp.Ticks)
.Y(dp => dp.Value);
SeriesCollection = new SeriesCollection(mapper);
var lineSeries = new LineSeries
{
Values = DataPoints.AsChartValues(),
Fill = Brushes.Transparent
};
SeriesCollection.Add(lineSeries);
TimeStampMin = DataPoints.FirstOrDefault().TimeStamp;
TimeStampMax = DataPoints.LastOrDefault().TimeStamp;
Formatter = value => new DateTime((long)value).ToString("MM/dd/yy HH:mm:ss");
DataContext = this;
}
现在,当我 运行 使用鼠标缩放 in/out 时,我的时间戳的更新结束值将很好地反映在 DateTimePicker
控件上。但是,如果我尝试在 DateTimePicker
控件中设置值以缩放 in/out,它不会合作。当我尝试时,在我的 Output
window 中,我收到以下两条与绑定相关的错误消息:
System.Windows.Data Error: 5 : Value produced by BindingExpression is
not valid for target property.; Value='10/09/2019 15:50:41'
BindingExpression:Path=TimeStampMin; DataItem='MainWindow' (Name='');
target element is 'Axis' (Name=''); target property is 'MinValue'
(type 'Double')
System.Windows.Data Error: 5 : Value produced by
BindingExpression is not valid for target property.; Value='10/09/2019
16:00:54' BindingExpression:Path=TimeStampMax; DataItem='MainWindow'
(Name=''); target element is 'Axis' (Name=''); target property is
'MaxValue' (type 'Double')
这告诉我问题出在绑定上,Axis.MinValue
和 Axis.MaxValue
期待双倍,而我的 TimeStampMin
和 TimeStampMax
显然是 DateTime
对象。我应该如何转换才能实现双向缩放?
Here 是我的全部 MainWindow
代码,如果你想重现的话。我正在为命令等使用 MVVMLight
工具包,所以如果你想 运行 按原样使用,你可能需要获取 NuGet
包。
看起来有些人无法访问 link 所以这里是完整的代码:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public SeriesCollection SeriesCollection
{
get;
set;
}
public Func<double, string> Formatter
{
get;
set;
}
private DateTime _timeStampMin;
public DateTime TimeStampMin
{
get
{
return _timeStampMin;
}
set
{
if (_timeStampMin == value)
return;
_timeStampMin = value;
OnPropertyChanged("TimeStampMin");
}
}
private DateTime _timeStampMax;
public DateTime TimeStampMax
{
get
{
return _timeStampMax;
}
set
{
if (_timeStampMax == value)
return;
_timeStampMax = value;
OnPropertyChanged("TimeStampMax");
}
}
public List<DataPoint> DataPoints
{
get;
set;
}
public RelayCommand<PreviewRangeChangedEventArgs> XRangeChangedCommand
{
get;
private set;
}
public MainWindow()
{
InitializeComponent();
XRangeChangedCommand = new RelayCommand<PreviewRangeChangedEventArgs>(e => XRangeChanged(e));
InitializeData();
PlotGraph();
}
private void InitializeData()
{
var now = DateTime.Now;
DataPoints = new List<DataPoint>{new DataPoint()
{Value = 1, TimeStamp = now.AddMinutes(1)}, new DataPoint()
{Value = 4, TimeStamp = now.AddMinutes(2)}, new DataPoint()
{Value = 9, TimeStamp = now.AddMinutes(3)}, new DataPoint()
{Value = 16, TimeStamp = now.AddMinutes(4)}, new DataPoint()
{Value = 25, TimeStamp = now.AddMinutes(5)}, new DataPoint()
{Value = 36, TimeStamp = now.AddMinutes(6)}, new DataPoint()
{Value = 49, TimeStamp = now.AddMinutes(7)}, new DataPoint()
{Value = 64, TimeStamp = now.AddMinutes(8)}, new DataPoint()
{Value = 81, TimeStamp = now.AddMinutes(9)}, new DataPoint()
{Value = 100, TimeStamp = now.AddMinutes(10)}, new DataPoint()
{Value = 11 * 11, TimeStamp = now.AddMinutes(11)}, new DataPoint()
{Value = 12 * 12, TimeStamp = now.AddMinutes(12)}, new DataPoint()
{Value = 13 * 13, TimeStamp = now.AddMinutes(13)}, new DataPoint()
{Value = 14 * 14, TimeStamp = now.AddMinutes(14)}, new DataPoint()
{Value = 15 * 15, TimeStamp = now.AddMinutes(15)}, new DataPoint()
{Value = 16 * 16, TimeStamp = now.AddMinutes(16)}, new DataPoint()
{Value = 17 * 17, TimeStamp = now.AddMinutes(17)}, new DataPoint()
{Value = 18 * 18, TimeStamp = now.AddMinutes(18)}, new DataPoint()
{Value = 19 * 19, TimeStamp = now.AddMinutes(19)}, new DataPoint()
{Value = 20 * 20, TimeStamp = now.AddMinutes(20)}, };
}
private void PlotGraph()
{
var mapper = Mappers.Xy<DataPoint>().X(dp => dp.TimeStamp.Ticks).Y(dp => dp.Value);
SeriesCollection = new SeriesCollection(mapper);
var lineSeries = new LineSeries{Values = DataPoints.AsChartValues(), Fill = Brushes.Transparent};
SeriesCollection.Add(lineSeries);
TimeStampMin = DataPoints.FirstOrDefault().TimeStamp;
TimeStampMax = DataPoints.LastOrDefault().TimeStamp;
Formatter = value => new DateTime((long)value).ToString("MM/dd/yy HH:mm:ss");
DataContext = this;
}
public void XRangeChanged(PreviewRangeChangedEventArgs e)
{
TimeStampMin = DateTime.FromBinary((long)e.PreviewMinValue);
TimeStampMax = DateTime.FromBinary((long)e.PreviewMaxValue);
}
}
是的,当您说问题出在 X 轴的最小值和最大值的双精度和日期时间之间时,您有部分答案:
您有两个解决方案:要么使用转换器,要么将日期选择器和 min/max 值之间的值分开:这里是第二个解决方案:
在 xaml 文件中:
<lvc:CartesianChart.AxisX>
<lvc:Axis LabelFormatter="{Binding Formatter}"
PreviewRangeChangedCommand="{Binding XRangeChangedCommand}"
MinValue="{Binding TimeStampMinX, Mode=TwoWay}"
MaxValue="{Binding TimeStampMaxX, Mode=TwoWay}"/>
</lvc:CartesianChart.AxisX>
在cs.file中:
private double _timeStampMinX;
public double TimeStampMinX
{
get
{
return _timeStampMinX;
}
set
{
if (_timeStampMinX == value)
return;
_timeStampMinX = value;
OnPropertyChanged("TimeStampMinX");
}
}
private double _timeStampMaxX;
public double TimeStampMaxX
{
get
{
return _timeStampMaxX;
}
set
{
if (_timeStampMaxX == value)
return;
_timeStampMaxX = value;
OnPropertyChanged("TimeStampMaxX");
}
}
private DateTime _timeStampMin;
public DateTime TimeStampMin
{
get
{
return _timeStampMin;
}
set
{
if (_timeStampMin == value)
return;
_timeStampMin = value;
TimeStampMinX = value.Ticks;
OnPropertyChanged("TimeStampMin");
}
}
private DateTime _timeStampMax;
public DateTime TimeStampMax
{
get
{
return _timeStampMax;
}
set
{
if (_timeStampMax == value)
return;
_timeStampMax = value;
TimeStampMaxX = value.Ticks;
OnPropertyChanged("TimeStampMax");
}
}
DateTime 和 double 值之间的 link 是 DateTime.Ticks。
带转换器的解决方案:
class转换器:
public class DateTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((DateTime)value).Ticks;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
转换器在xaml文件中的集成(使用您的命名空间更改命名空间WpfApp2)
xmlns:dc="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<dc:DateTimeConverter x:Key="DateTimeConverter"></dc:DateTimeConverter>
</Window.Resources>
:
:
<lvc:CartesianChart.AxisX>
<lvc:Axis LabelFormatter="{Binding Formatter}"
PreviewRangeChangedCommand="{Binding XRangeChangedCommand}"
MinValue="{Binding TimeStampMin, Mode=TwoWay,Converter={StaticResource DateTimeConverter}}"
MaxValue="{Binding TimeStampMax, Mode=TwoWay,Converter={StaticResource DateTimeConverter}}"/>
</lvc:CartesianChart.AxisX>
在这种情况下不需要分离绑定值,转换器会完成这项工作。
我有一个使用 LiveCharts
的 WPF
应用程序,我在其中绘制了一个 LineGraph
,X 轴为 DateTime
。我的最终目标是实现 'two-way' 缩放。也就是说,我的应用程序上有两个 DateTimePicker
控件(来自 WPF Toolkit),它们代表 当前显示区域 的最小值和最大值 DateTime
图形,如果我在图形区域上使用滚轮缩放 in/out,更新的范围应该反映在所述控件上,并且(这是我正在努力的部分)相反,如果我设置 min/max 在 DateTimePicker
控件上,图形应相应地缩放 in/out。
我的XAML
很简单:
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<lvc:CartesianChart Name="MyChart"
Series="{Binding SeriesCollection}"
Zoom="X">
<lvc:CartesianChart.AxisX>
<lvc:Axis LabelFormatter="{Binding Formatter}"
PreviewRangeChangedCommand="{Binding XRangeChangedCommand}"
MinValue="{Binding TimeStampMin, Mode=TwoWay}"
MaxValue="{Binding TimeStampMax, Mode=TwoWay}"/>
</lvc:CartesianChart.AxisX>
</lvc:CartesianChart>
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
<xceed:DateTimePicker Margin="4" Width="160" Name="dtpMinX"
Format="Custom" FormatString="yyyy/MM/dd HH:mm:ss"
Value="{Binding TimeStampMin, Mode=TwoWay}"/>
<xceed:DateTimePicker Margin="4" Width="160" Name="dtpMaxX"
Format="Custom" FormatString="yyyy/MM/dd HH:mm:ss"
Value="{Binding TimeStampMax, Mode=TwoWay}"/>
</StackPanel>
</Grid>
这是我的 DataPoint
class:
public class DataPoint
{
public DataPoint() { }
public DataPoint(DateTime timeStamp, double value)
{
TimeStamp = timeStamp;
Value = value;
}
public double Value { get; set; }
public DateTime TimeStamp { get; set; }
}
这是我的 PlotGraph()
方法,可以完成所有绘图工作。如果您想重现此应用程序,我会将我的整个 MainWindow()
代码包含在此 post 的底部。
private void PlotGraph()
{
var mapper = Mappers.Xy<DataPoint>()
.X(dp => dp.TimeStamp.Ticks)
.Y(dp => dp.Value);
SeriesCollection = new SeriesCollection(mapper);
var lineSeries = new LineSeries
{
Values = DataPoints.AsChartValues(),
Fill = Brushes.Transparent
};
SeriesCollection.Add(lineSeries);
TimeStampMin = DataPoints.FirstOrDefault().TimeStamp;
TimeStampMax = DataPoints.LastOrDefault().TimeStamp;
Formatter = value => new DateTime((long)value).ToString("MM/dd/yy HH:mm:ss");
DataContext = this;
}
现在,当我 运行 使用鼠标缩放 in/out 时,我的时间戳的更新结束值将很好地反映在 DateTimePicker
控件上。但是,如果我尝试在 DateTimePicker
控件中设置值以缩放 in/out,它不会合作。当我尝试时,在我的 Output
window 中,我收到以下两条与绑定相关的错误消息:
System.Windows.Data Error: 5 : Value produced by BindingExpression is not valid for target property.; Value='10/09/2019 15:50:41' BindingExpression:Path=TimeStampMin; DataItem='MainWindow' (Name=''); target element is 'Axis' (Name=''); target property is 'MinValue' (type 'Double')
System.Windows.Data Error: 5 : Value produced by BindingExpression is not valid for target property.; Value='10/09/2019 16:00:54' BindingExpression:Path=TimeStampMax; DataItem='MainWindow' (Name=''); target element is 'Axis' (Name=''); target property is 'MaxValue' (type 'Double')
这告诉我问题出在绑定上,Axis.MinValue
和 Axis.MaxValue
期待双倍,而我的 TimeStampMin
和 TimeStampMax
显然是 DateTime
对象。我应该如何转换才能实现双向缩放?
Here 是我的全部 MainWindow
代码,如果你想重现的话。我正在为命令等使用 MVVMLight
工具包,所以如果你想 运行 按原样使用,你可能需要获取 NuGet
包。
看起来有些人无法访问 link 所以这里是完整的代码:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public SeriesCollection SeriesCollection
{
get;
set;
}
public Func<double, string> Formatter
{
get;
set;
}
private DateTime _timeStampMin;
public DateTime TimeStampMin
{
get
{
return _timeStampMin;
}
set
{
if (_timeStampMin == value)
return;
_timeStampMin = value;
OnPropertyChanged("TimeStampMin");
}
}
private DateTime _timeStampMax;
public DateTime TimeStampMax
{
get
{
return _timeStampMax;
}
set
{
if (_timeStampMax == value)
return;
_timeStampMax = value;
OnPropertyChanged("TimeStampMax");
}
}
public List<DataPoint> DataPoints
{
get;
set;
}
public RelayCommand<PreviewRangeChangedEventArgs> XRangeChangedCommand
{
get;
private set;
}
public MainWindow()
{
InitializeComponent();
XRangeChangedCommand = new RelayCommand<PreviewRangeChangedEventArgs>(e => XRangeChanged(e));
InitializeData();
PlotGraph();
}
private void InitializeData()
{
var now = DateTime.Now;
DataPoints = new List<DataPoint>{new DataPoint()
{Value = 1, TimeStamp = now.AddMinutes(1)}, new DataPoint()
{Value = 4, TimeStamp = now.AddMinutes(2)}, new DataPoint()
{Value = 9, TimeStamp = now.AddMinutes(3)}, new DataPoint()
{Value = 16, TimeStamp = now.AddMinutes(4)}, new DataPoint()
{Value = 25, TimeStamp = now.AddMinutes(5)}, new DataPoint()
{Value = 36, TimeStamp = now.AddMinutes(6)}, new DataPoint()
{Value = 49, TimeStamp = now.AddMinutes(7)}, new DataPoint()
{Value = 64, TimeStamp = now.AddMinutes(8)}, new DataPoint()
{Value = 81, TimeStamp = now.AddMinutes(9)}, new DataPoint()
{Value = 100, TimeStamp = now.AddMinutes(10)}, new DataPoint()
{Value = 11 * 11, TimeStamp = now.AddMinutes(11)}, new DataPoint()
{Value = 12 * 12, TimeStamp = now.AddMinutes(12)}, new DataPoint()
{Value = 13 * 13, TimeStamp = now.AddMinutes(13)}, new DataPoint()
{Value = 14 * 14, TimeStamp = now.AddMinutes(14)}, new DataPoint()
{Value = 15 * 15, TimeStamp = now.AddMinutes(15)}, new DataPoint()
{Value = 16 * 16, TimeStamp = now.AddMinutes(16)}, new DataPoint()
{Value = 17 * 17, TimeStamp = now.AddMinutes(17)}, new DataPoint()
{Value = 18 * 18, TimeStamp = now.AddMinutes(18)}, new DataPoint()
{Value = 19 * 19, TimeStamp = now.AddMinutes(19)}, new DataPoint()
{Value = 20 * 20, TimeStamp = now.AddMinutes(20)}, };
}
private void PlotGraph()
{
var mapper = Mappers.Xy<DataPoint>().X(dp => dp.TimeStamp.Ticks).Y(dp => dp.Value);
SeriesCollection = new SeriesCollection(mapper);
var lineSeries = new LineSeries{Values = DataPoints.AsChartValues(), Fill = Brushes.Transparent};
SeriesCollection.Add(lineSeries);
TimeStampMin = DataPoints.FirstOrDefault().TimeStamp;
TimeStampMax = DataPoints.LastOrDefault().TimeStamp;
Formatter = value => new DateTime((long)value).ToString("MM/dd/yy HH:mm:ss");
DataContext = this;
}
public void XRangeChanged(PreviewRangeChangedEventArgs e)
{
TimeStampMin = DateTime.FromBinary((long)e.PreviewMinValue);
TimeStampMax = DateTime.FromBinary((long)e.PreviewMaxValue);
}
}
是的,当您说问题出在 X 轴的最小值和最大值的双精度和日期时间之间时,您有部分答案:
您有两个解决方案:要么使用转换器,要么将日期选择器和 min/max 值之间的值分开:这里是第二个解决方案:
在 xaml 文件中:
<lvc:CartesianChart.AxisX>
<lvc:Axis LabelFormatter="{Binding Formatter}"
PreviewRangeChangedCommand="{Binding XRangeChangedCommand}"
MinValue="{Binding TimeStampMinX, Mode=TwoWay}"
MaxValue="{Binding TimeStampMaxX, Mode=TwoWay}"/>
</lvc:CartesianChart.AxisX>
在cs.file中:
private double _timeStampMinX;
public double TimeStampMinX
{
get
{
return _timeStampMinX;
}
set
{
if (_timeStampMinX == value)
return;
_timeStampMinX = value;
OnPropertyChanged("TimeStampMinX");
}
}
private double _timeStampMaxX;
public double TimeStampMaxX
{
get
{
return _timeStampMaxX;
}
set
{
if (_timeStampMaxX == value)
return;
_timeStampMaxX = value;
OnPropertyChanged("TimeStampMaxX");
}
}
private DateTime _timeStampMin;
public DateTime TimeStampMin
{
get
{
return _timeStampMin;
}
set
{
if (_timeStampMin == value)
return;
_timeStampMin = value;
TimeStampMinX = value.Ticks;
OnPropertyChanged("TimeStampMin");
}
}
private DateTime _timeStampMax;
public DateTime TimeStampMax
{
get
{
return _timeStampMax;
}
set
{
if (_timeStampMax == value)
return;
_timeStampMax = value;
TimeStampMaxX = value.Ticks;
OnPropertyChanged("TimeStampMax");
}
}
DateTime 和 double 值之间的 link 是 DateTime.Ticks。
带转换器的解决方案:
class转换器:
public class DateTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((DateTime)value).Ticks;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
转换器在xaml文件中的集成(使用您的命名空间更改命名空间WpfApp2)
xmlns:dc="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<dc:DateTimeConverter x:Key="DateTimeConverter"></dc:DateTimeConverter>
</Window.Resources>
:
:
<lvc:CartesianChart.AxisX>
<lvc:Axis LabelFormatter="{Binding Formatter}"
PreviewRangeChangedCommand="{Binding XRangeChangedCommand}"
MinValue="{Binding TimeStampMin, Mode=TwoWay,Converter={StaticResource DateTimeConverter}}"
MaxValue="{Binding TimeStampMax, Mode=TwoWay,Converter={StaticResource DateTimeConverter}}"/>
</lvc:CartesianChart.AxisX>
在这种情况下不需要分离绑定值,转换器会完成这项工作。