LiveCharts устанавливают DateTime X-Axis MinValue и MaxValue для увеличения / уменьшения масштаба - PullRequest
1 голос
/ 10 октября 2019

У меня есть приложение WPF, использующее LiveCharts, где я строю LineGraph с осью X, равной DateTime. Моя конечная цель здесь - добиться «двустороннего» увеличения. То есть у меня есть два DateTimePicker элемента управления (из WPF Toolkit ) в моем приложении, которые представляют минимальный и максимальный DateTime отображаемой в данный момент области графика, и еслиЯ использую колесо прокрутки в области графика для увеличения / уменьшения масштаба, обновленный диапазон должен отражаться на указанных элементах управления, и (с этой частью я борюсь), наоборот, если я установил min / max на DateTimePickerуправления, график должен соответственно увеличиваться / уменьшаться.

Мой 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 класс:

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() внизу этого поста, если вы хотите воспроизвести это приложение.

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;
}

Теперь, когда я запускаю это и увеличиваю / уменьшаю масштаб с помощью мыши, обновленное конечное значениемои метки времени будут отлично отображаться на элементах управления DateTimePicker. Однако, если я попытаюсь установить значение в элементах управления DateTimePicker для увеличения / уменьшения, оно не будет работать. При попытке в моем окне Output я получаю два следующих сообщения об ошибках, связанных с привязкой:

System.Windows.Data Ошибка: 5: значение, созданное BindingExpression, недопустимо для целевого свойства. ;Значение = '10 / 09/2019 15:50:41 'BindingExpression: Path = TimeStampMin;DataItem = 'MainWindow' (Name = '');целевым элементом является «Ось» (Name = '');свойство target равно MinValue (тип Double)

System.Windows.Data Ошибка: 5: значение, созданное BindingExpression, недопустимо для целевого свойства .;Значение = '10 / 09/2019 16:00:54 'BindingExpression: Path = TimeStampMax;DataItem = 'MainWindow' (Name = '');целевым элементом является «Ось» (Name = '');Свойство target - MaxValue (тип Double)

Это говорит мне, что проблема в привязке, что Axis.MinValue и Axis.MaxValue ожидают двойного значения, тогда как мои TimeStampMin и TimeStampMax - это, очевидно, DateTime объекты. Как бы я сделал преобразование, чтобы получить двустороннее увеличение?


Здесь - это весь мой код MainWindow, если вы хотите его воспроизвести. Я использую MVVMLight инструментарий для команд и т. Д., Поэтому вам может потребоваться получить пакет NuGet, если вы хотите запустить его как есть.

Похоже, что некоторые люди не могут получить доступ к ссылке, поэтомувот полный код:

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);
    }
}

1 Ответ

1 голос
/ 11 октября 2019

Да, у вас была часть ответа, когда вы сказали, что проблема была между double и datetime для значения Min и Max для оси X:

У вас есть два решения: либо использовать Converter, либо разделить значение междуDatePicker и 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 идвойное значение - DateTime.Ticks.


решение с преобразователем:

преобразователь класса:

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>

, в этом случае нет необходимости разделять значение привязки, конвертер выполняет свою работу.

...