using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; using C1.WPF.Chart; using C1.WPF.Chart.Interaction; using DTS.Common.Base; using DTS.Common.Behaviors; using DTS.Common.Enums.Viewer; using DTS.Common.Events; using DTS.Common.Interface; using Microsoft.Practices.Prism; using Microsoft.Practices.Prism.Commands; using Microsoft.Practices.Prism.Events; using Microsoft.Practices.Prism.Interactivity.InteractionRequest; using Microsoft.Practices.Prism.Regions; using Microsoft.Practices.Prism.UnityExtensions; using Microsoft.Practices.Unity; using Axis = C1.WPF.C1Chart.Axis; using RenderMode = C1.WPF.Chart.RenderMode; // ReSharper disable RedundantDefaultMemberInitializer // ReSharper disable UnusedParameter.Local // ReSharper disable InconsistentNaming // ReSharper disable NotAccessedField.Local // ReSharper disable AutoPropertyCanBeMadeGetOnly.Local // ReSharper disable CheckNamespace // ReSharper disable CompareOfFloatsByEqualityOperator // ReSharper disable RedundantAssignment namespace DTS.Viewer.Graph { public class TestDataViewModel : BaseViewModel, ITestDataViewModel, IMouseCaptureProxy { public ITestDataView View { get; set; } private IBaseViewModel Parent { get; set; } public new ITestDataSeries Model { get; set; } private IEventAggregator _eventAggregator { get; set; } private new IRegionManager _regionManager; private IUnityContainer _unityContainer { get; set; } public InteractionRequest NotificationRequest { get; private set; } public new InteractionRequest ConfirmationRequest { get; private set; } private IChartOptionsModel ChartViewOptions { get; set; } private bool initLoad = false; /// /// Creates a new instance of the GraphViewModel. /// /// The GraphView interface. /// The logical placeholder defined within the application's UI (in the shell or within views) into which views are displayed. /// The EventAggregator which allows different components to publish/subscribe to events without being coupled to each other. /// The unityContainer. public TestDataViewModel(ITestDataView view, IRegionManager regionManager, IEventAggregator eventAggregator, IUnityContainer unityContainer) : base(regionManager, eventAggregator, unityContainer) { View = view; View.DataContext = this; NotificationRequest = new InteractionRequest(); ConfirmationRequest = new InteractionRequest(); _eventAggregator = eventAggregator; _unityContainer = unityContainer; _regionManager = regionManager; } #region Methods public override void Initialize() { } public override void Initialize(object parameter) { Subscribe(); Parent = (IBaseViewModel)parameter; ChartViewOptions = _unityContainer.TryResolve(); GraphDataSeries.CollectionChanged += GraphDataSeries_CollectionChanged; initLoad = true; } #region Collection Changed public void GraphDataSeries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.OldItems != null) { foreach (INotifyPropertyChanged item in e.OldItems) item.PropertyChanged -= GraphDataSeries_PropertyChanged; } if (e.NewItems != null) { foreach (INotifyPropertyChanged item in e.NewItems) item.PropertyChanged += GraphDataSeries_PropertyChanged; } //IsFilterEnabled = _testSummaryList.Count > 0; //FilteredTestSummaryList = new ObservableCollection(_testSummaryList); } private void GraphDataSeries_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "TestSummaryList") { //IsFilterEnabled = _testSummaryList.Count > 0; //FilteredTestSummaryList = new ObservableCollection(_testSummaryList); //OnPropertyChanged("TestSummaryList"); } } #endregion Collection Changed public override void Activated() { var fp = new FilterParameterArgs { Param = String.Empty, Requester = this }; _eventAggregator.GetEvent().Publish(fp); } public override void Cleanup() { } public new Task CleanupAsync() { return null; } public override Task InitializeAsync() { return null; } public override Task InitializeAsync(object parameter) { return null; } #endregion private void Subscribe() { _eventAggregator.GetEvent().Subscribe(OnChartOptionsChanged); //_eventAggregator.GetEvent().Subscribe(OnLockedChannelChanged); _eventAggregator.GetEvent().Subscribe(OnLockedChannelsChanged); _eventAggregator.GetEvent().Subscribe(OnSelectedChannelsChanged); _eventAggregator.GetEvent().Subscribe(OnResetZoom); _eventAggregator.GetEvent().Subscribe(OnGraphClearChanged); _eventAggregator.GetEvent().Subscribe(OnShowCursorChanged); _eventAggregator.GetEvent().Subscribe(OnClearCursorChanged); _eventAggregator.GetEvent().Subscribe(OnCursorShowMinMaxChanged); _eventAggregator.GetEvent().Subscribe(OnCursorShowT0Changed); } #region Markers private void OnCursorShowMinMaxChanged(bool value) { } private void OnCursorShowT0Changed(bool value) { } /// /// Clear all markers /// /// private void OnClearCursorChanged(bool clear) { var list = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.Where(layer => layer.GetType() == typeof(C1LineMarker)).ToList(); foreach (var layer in list) { ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.Remove(layer); } _eventAggregator.GetEvent().Publish(false); } /// /// Show/hide markers /// /// private void OnShowCursorChanged(bool show) { //if (initLoad) //{ // var oneThird = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PlotRect.Right * 0.3; // var twoThird = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PlotRect.Right * 0.6; // ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.Add(CreateMarker(new Point(oneThird, 0))); // ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.Add(CreateMarker(new Point(twoThird, 0))); // _eventAggregator.GetEvent().Publish(true); // initLoad = false; //} ChartViewOptions.CanPublishChanges = false; ChartViewOptions.ShowCursor = show; ShowCursor = show; ChartViewOptions.CanPublishChanges = true; if (((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.Count == 0) return; foreach (var layer in ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers) { if (layer.GetType() != typeof(C1LineMarker)) continue; ((C1LineMarker)layer).Visibility = show ? Visibility.Visible : Visibility.Hidden; } } #endregion Markers #region Zoom Change Events /// /// Reset UI Zoom /// /// Reset all or reset X private void OnResetZoom(bool all) { if (GraphDataSeries.Count == 0) return; if (all) ResetZoom(); else ResetT(); } #endregion Zoom Change Events #region Chart Options Changed Event public void OnGraphClearChanged(bool args) { GraphDataSeries = new ObservableCollection(); TestChannelList = new ObservableCollection(); ChartViewOptions.CanPublishChanges = false; ChartViewOptions.ReadData = true; ChartViewOptions.CanPublishChanges = true; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Series.Clear(); } /// /// Chart Options Changed Event /// /// IChartOptionsModel public void OnChartOptionsChanged(IChartOptionsModel args) { ChartViewOptions = args; if (ChartViewOptions.ReadData) { ReadData(ChartViewOptions, TestChannelList.ToList()); ChartViewOptions.ReadData = false; } else { ApplyChartOptions(ChartViewOptions); } } #endregion Chart Options Changed Event #region Graph Selection Changed event private void OnSelectedChannelsChanged(List channels) { if (TestChannelList == null) TestChannelList = new ObservableCollection(); if (SelectedChannels == null) { SelectedChannels = new List(); } RemoveSelectedChannels(SelectedChannels); SelectedChannels = channels; TestChannelList.AddRange(channels); ReadData(ChartViewOptions, TestChannelList.ToList()); } private void RemoveSelectedChannels(List channels) { foreach (var channel in channels) { if (TestChannelList.Any(x => x.TestId == channel.TestId && x.ChannelId == channel.ChannelId)) { TestChannelList.Remove(channel); if (GraphDataSeries != null && GraphDataSeries.Any(x => x.TestId == channel.TestId && x.ChannelId == channel.ChannelId)) { var item = GraphDataSeries.SingleOrDefault(x => x.TestId == channel.TestId && x.ChannelId == channel.ChannelId); if (item != null) { GraphDataSeries.Remove(item); //TODO: review DisplayGraphs(GraphDataSeries); } } } } } private void OnLockedChannelsChanged(List channels) { if (TestChannelList == null) { GraphDataSeries = new ObservableCollection(); TestChannelList = new ObservableCollection(); } RemoveSelectedChannels(SelectedChannels); SelectedChannels = new List(); var readData = false; foreach (var channel in channels) { if (channel.IsLocked) { if (TestChannelList == null) { TestChannelList = new ObservableCollection(); } TestChannelList.Add(channel); readData = true; } else { TestChannelList.Remove(channel); var item = GraphDataSeries.SingleOrDefault(x => x.TestId == channel.TestId && x.ChannelId == channel.ChannelId); if (item != null) GraphDataSeries.Remove(item); DisplayGraphs(GraphDataSeries); } } if (readData) { ReadData(ChartViewOptions, TestChannelList.ToList()); } } #endregion Graph Selection Changed event #region Read Channel Data /// /// Read channel data /// /// /// private void ReadData(IChartOptionsModel chartOptions, List channels) { _eventAggregator.GetEvent().Publish(new StatusInfo(StatusInfo.StatusState.Busy, "Please wait...")); _eventAggregator.GetEvent().Publish(true); GraphDataSeries = new ObservableCollection(); if (channels == null || channels.Count == 0) return; //var worker = new BackgroundWorker(); //worker.DoWork += LoadTestDataSeries; //worker.RunWorkerCompleted += LoadTestDataSeriesCompleted; //worker.RunWorkerAsync(channels); #region Dispatcher Dispatcher.CurrentDispatcher.InvokeAsync(async () => { _eventAggregator.GetEvent().Publish(new StatusInfo(StatusInfo.StatusState.Busy, "Please wait...")); try { _eventAggregator.GetEvent().Publish(new StatusInfo(StatusInfo.StatusState.Busy, "Please wait. Loading data")); var dataSeries = await new TestDataSeriesModel().GetTestDataAsync(channels, chartOptions); ChartViewOptions.CanPublishChanges = false; if (!ChartViewOptions.LockedT) { ChartViewOptions.MaxFixedT = dataSeries.Max(x => x.Xvalue.Max()); ChartViewOptions.MinFixedT = dataSeries.Min(x => x.Xvalue.Min()); CurrentXMax = ChartViewOptions.MaxFixedT; CurrentXMin = ChartViewOptions.MinFixedT; } ChartViewOptions.CanPublishChanges = true; GraphDataSeries = new ObservableCollection(dataSeries); DisplayGraphs(GraphDataSeries); ApplyChartOptions(chartOptions); //((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.UpdateLayout(); _eventAggregator.GetEvent().Publish(new StatusInfo(StatusInfo.StatusState.Idle, String.Empty)); _eventAggregator.GetEvent().Publish(false); } catch (Exception ex) { MessageBox.Show("Error", ex.Message, MessageBoxButton.OK); _eventAggregator.GetEvent().Publish(new StatusInfo(StatusInfo.StatusState.Idle, String.Empty)); _eventAggregator.GetEvent().Publish(false); } }, DispatcherPriority.Background); #endregion Dispatcher } #endregion #region Properties private Visibility _pointLabelVisibilty = Visibility.Visible; public Visibility PointLabelVisibilty { get => _pointLabelVisibilty; set { _pointLabelVisibilty = value; OnPropertyChanged("PointLabelVisibilty"); } } private List _selectedChannels = null; public List SelectedChannels { get => _selectedChannels; set { _selectedChannels = value; OnPropertyChanged("SelectedChannels"); } } private ObservableCollection _testChannelList; public ObservableCollection TestChannelList { get => _testChannelList; set { _testChannelList = value; OnPropertyChanged("TestChannelList"); } } public ObservableCollection _graphDataSeries = new ObservableCollection(); public ObservableCollection GraphDataSeries { get => _graphDataSeries; set { _graphDataSeries = value; OnPropertyChanged("GraphDataSeries"); } } private ObservableCollection _testDataSeries = new ObservableCollection(); public ObservableCollection TestDataSeries { get => _testDataSeries; set { _testDataSeries = value; OnPropertyChanged("TestDataSeries"); } } #region Graph private string _titleY = String.Empty; public string TitleY { get => _titleY; set { _titleY = value; OnPropertyChanged("TitleY"); } } private string _titleX = String.Empty; public string TitleX { get => _titleX; set { _titleX = value; OnPropertyChanged("TitleX"); } } public double _currentYMin = 0D; public double CurrentYMin { get => _currentYMin; set { _currentYMin = value; if (CanPublishChanges) _eventAggregator.GetEvent().Publish(new ChartAxisChangedEventArg() { Axis = "Y", MinValue = _currentYMin, MaxValue = _currentYMax }); OnPropertyChanged("CurrentYMin"); } } private double _currentYMax = 0D; public double CurrentYMax { get => _currentYMax; set { _currentYMax = value; if (CanPublishChanges) _eventAggregator.GetEvent().Publish(new ChartAxisChangedEventArg() { Axis = "Y", MinValue = _currentYMin, MaxValue = _currentYMax }); OnPropertyChanged("CurrentYMax"); } } public double _currentXMin = 0D; public double CurrentXMin { get => _currentXMin; set { _currentXMin = value; if (CanPublishChanges) _eventAggregator.GetEvent().Publish(new ChartAxisChangedEventArg() { Axis = "X", MinValue = _currentXMin, MaxValue = _currentXMax }); OnPropertyChanged("CurrentXMin"); } } private double _currentXMax = 0D; public double CurrentXMax { get => _currentXMax; set { _currentXMax = value; if (CanPublishChanges) _eventAggregator.GetEvent().Publish(new ChartAxisChangedEventArg() { Axis = "X", MinValue = _currentXMin, MaxValue = _currentXMax }); OnPropertyChanged("CurrentXMax"); } } private bool _canPublishChanges = true; public bool CanPublishChanges { get => _canPublishChanges; set { _canPublishChanges = value; OnPropertyChanged("CanPublishChanges"); } } private bool _showCursor = true; public bool ShowCursor { get => ChartViewOptions.ShowCursor; set { _showCursor = value; OnPropertyChanged("ShowCursor"); } } #endregion Graph private bool _isBusy = false; public new bool IsBusy { get => _isBusy; set { _isBusy = value; OnPropertyChanged("IsBusy"); } } public new bool IsDirty { get; } = false; private Cursor _chartCursor = Cursors.Arrow; public Cursor ChartCursor { get => _chartCursor; set { _chartCursor = value; OnPropertyChanged("ChartCursor"); } } /// ///Occurs when a property value changes. /// public new event PropertyChangedEventHandler PropertyChanged; private new void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } #endregion Properties #region UI functions /// /// Apply Chart Options /// /// private void ApplyChartOptions(IChartOptionsModel chartOptions) { if (GraphDataSeries.Count == 0) return; //var view = (TestDataView)((IGraphViewModel)Parent).DataSeriesView; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisX.Max = ChartViewOptions.MaxFixedT; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisX.Min = ChartViewOptions.MinFixedT; //MainChart.AxisX.Max = ChartOptions.MaxFixedT; //MainChart.AxisX.Min = ChartOptions.MinFixedT; if (chartOptions.LockedT) { //view.MainChart.AxisX.Scale = 1; ScaleChart(); } else { chartOptions.CanPublishChanges = false; ScaleChart(); chartOptions.CanPublishChanges = true; } SetAxisTitle(chartOptions); UpdateScrollbars(); } bool zooming = false; Point ptStart = new Point(); /// /// Display Graphs UI /// /// Charts to display private void DisplayGraphs(ObservableCollection graphs) { ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.CollectionChanged -= MainChartLayers_CollectionChanged; //((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.KeyDown -= MainChart_KeyDown; //((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Rendered -= MainChart_Rendered; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Series.Clear(); ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Background = new SolidColorBrush(Colors.White); ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Foreground = new SolidColorBrush(Colors.Blue); foreach (var graph in graphs) { var style = new ChartStyle { Stroke = graph.GraphColor, StrokeThickness = 1, Fill = graph.GraphColor }; var pc = new PointCollection(); for (var i = 0; i < graph.Xvalue.Count; i++) { pc.Add(new Point(graph.Xvalue[i], graph.Yvalue[i])); } var series = new Series { SeriesName = graph.ChannelId, Binding = "Y", BindingX = "X", ItemsSource = pc, Style = style }; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Series.Add(series); } ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AllowDrop = true; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisX.Scrollbar = new C1AxisScrollbar { ScrollButtonsVisible = false, Width = 22 }; ((TestDataView) ((IGraphViewModel) Parent).DataSeriesView).MainChart.AxisY.Scrollbar = new C1AxisScrollbar {ScrollButtonsVisible = false, Width = 22 }; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.CollectionChanged += MainChartLayers_CollectionChanged; //((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.KeyDown += MainChart_KeyDown; //((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Rendered += MainChart_Rendered; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.LegendToggle = true; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.RenderMode = RenderMode.Direct2D; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.UpdateLayout(); } //private void MainChart_Rendered(object sender, RenderEventArgs e) //{ // ReDrawMrkers(); //} #region Zoom public event EventHandler Capture; public event EventHandler Release; private Point _currentPoint; public void OnMouseDown(object sender, MouseCaptureArgs e) { var left = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PlotRect.Left; var right = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PlotRect.Right; var top = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PlotRect.Top; var bottom = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PlotRect.Bottom; if (e.X > right || e.X < left) return; if (e.Y > bottom || e.Y < top) return; if (e.RightButton) { if (!ChartViewOptions.ShowCursor) return; _currentPoint = new Point(e.X, e.Y); } else if (e.LeftButton) { if (!zooming) { var marker = FindMarker(new Point(e.X, e.Y)); if (marker != null) { marker.Focus(); return; } } ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).ReversibleFrameContainer.Visibility = Visibility.Visible; // Start zooming zooming = true; // Save starting point of selection rectangle ptStart = new Point(e.X, e.Y); ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.CaptureMouse(); } } public void OnMouseMove(object sender, MouseCaptureArgs e) { if (!e.LeftButton) return; // when zooming update selection range if (zooming) { // Draw new frame DrawReversibleRectangle(ptStart, new Point(e.X, e.Y)); } } public void OnMouseUp(object sender, MouseCaptureArgs e) { if (!zooming) return; //End zooming zooming = false; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).ReversibleFrameContainer.Visibility = Visibility.Collapsed; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).ReversibleFrame.Rect = new Rect(); var currentPosition = new Point(e.X, e.Y); PerformZoom(ptStart, currentPosition); //Clean up ptStart = new Point(); ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.ReleaseMouseCapture(); ChartCursor = Cursors.Arrow; } private void DrawReversibleRectangle(Point p1, Point p2) { // Normalize the rectangle var rc = new Rect(Math.Min(p1.X, p2.X), Math.Min(p1.Y, p2.Y), Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y)); // Draw the reversible frame ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).ReversibleFrame.Rect = rc; } private void PerformZoom(Point pointStart, Point pointLast) { var p1 = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PointToData(pointStart); var p2 = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PointToData(pointLast); var view = (TestDataView)((IGraphViewModel)Parent).DataSeriesView; if (ChartViewOptions.LockedT) { view.MainChart.AxisX.Max = ChartViewOptions.MaxFixedT; view.MainChart.AxisX.Min = ChartViewOptions.MinFixedT; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Min = Math.Min(p1.Y, p2.Y); ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Max = Math.Max(p1.Y, p2.Y); } else if (ChartViewOptions.LockedY) { view.MainChart.AxisY.Max = ChartViewOptions.MaxFixedY; view.MainChart.AxisY.Min = ChartViewOptions.MinFixedY; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisX.Min = Math.Min(p1.X, p2.X); ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisX.Max = Math.Max(p1.X, p2.X); } else { CurrentXMax = p2.X; CurrentXMin = p1.X; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.BeginUpdate(); // Update axes with new limits ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisX.Min = Math.Min(p1.X, p2.X); ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Min = Math.Min(p1.Y, p2.Y); ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisX.Max = Math.Max(p1.X, p2.X); ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Max = Math.Max(p1.Y, p2.Y); ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.EndUpdate(); } CurrentYMax = Math.Max(p1.Y, p2.Y); CurrentYMin = Math.Min(p1.Y, p2.Y); ChartViewOptions.CanPublishChanges = false; ChartViewOptions.MaxFixedT = CurrentXMax; ChartViewOptions.MinFixedT = CurrentXMin; ChartViewOptions.MaxFixedY = CurrentYMax; ChartViewOptions.MinFixedY = CurrentYMin; ChartViewOptions.YRange = YRangeScaleEnum.Manual; ChartViewOptions.CanPublishChanges = true; //ScaleChart(); UpdateScrollbars(); } #endregion Zoom private void MainChart_KeyDown(object sender, KeyEventArgs e) { var marker = FindMarker(); if (marker == null) return; switch (e.Key) { case Key.Delete: ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.Remove(marker); if (((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.Count == 0) { _eventAggregator.GetEvent().Publish(false); } return; case Key.Left: case Key.Right: e.Handled = true; var currentPosition = marker.Margin.Left; var bottom = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PlotRect.Bottom; var right = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PlotRect.Right; switch (e.Key) { case Key.Left: if (currentPosition > 0) { currentPosition--; } break; case Key.Right: if (currentPosition < right) { currentPosition++; } break; } marker.Margin = new Thickness(currentPosition, 0, 0, 0); var item = marker.Tag; marker.Content = CreateMarkerContent(marker, new Point(currentPosition, bottom)); marker.ToolTip = marker.Content; marker.Focus(); break; } } private void MainChartLayers_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { //foreach (var layer in ((TestDataView) ((IGraphViewModel) Parent).DataSeriesView).MainChart.Layers) //{ // ((C1LineMarker) layer).DragContent = true; // ((C1LineMarker)layer).DragLines = true; // ((C1LineMarker)layer).Visibility = Visibility.Visible; //} } private void SetContentAlignment() { if (((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.Count == 0) return; foreach (var layer in ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers) { if (layer.GetType() != typeof(C1LineMarker)) continue; ((C1LineMarker)layer).Alignment = LineMarkerAlignment.Top; } } private void ReDrawMrkers() { if (((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.Count == 0) return; var plotArea = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PlotRect; foreach (var layer in ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers) { if (layer.GetType() != typeof(C1LineMarker)) continue; var markerData = (Point)((TextBlock)((C1LineMarker)layer).Content).Tag; //if (point.X < plotArea.Left || point.X > plotArea.Right) { ((C1LineMarker)layer).Visibility = Visibility.Hidden; } if (markerData.X < ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisX.Min || markerData.X > ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisX.Max) { ((C1LineMarker)layer).Visibility = Visibility.Hidden; } else { var point = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PointToScreen(markerData); ((C1LineMarker)layer).Margin = new Thickness(point.X, 0, 0, 0); ((C1LineMarker)layer).Visibility = Visibility.Visible; } } } /// /// Create Marker /// /// /// http://helpcentral.componentone.com/nethelp/flexchartwpf/C1.WPF.FlexChart.4~C1.WPF.Chart.Interaction.C1LineMarker_members.html /// private C1LineMarker CreateMarker(Point pt) { var lm = new C1LineMarker { Lines = LineMarkerLines.Vertical, VerticalAlignment = VerticalAlignment.Center, Name = "LineMarker" + ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.Count, Width = 2, Height = Math.Abs(((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PlotRect.Bottom - ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PlotRect.Top), Interaction = LineMarkerInteraction.Drag, DragContent = true, DragLines = true, Background = new SolidColorBrush(Colors.Blue), Focusable = true, Margin = new Thickness(pt.X, 0, 0, 0), Visibility = Visibility.Visible, IsEnabled = true, HorizontalPosition = pt.X, VerticalPosition = 100 }; lm.Content = CreateMarkerContent(lm, pt); lm.ToolTip = lm.Content; lm.MouseDown += LineMarker_MouseDown; //lm.PositionChanged += LineMarker_PositionChanged; lm.GotFocus += LineMarker_GotFocus; lm.LostFocus += LineMarker_LostFocus; return lm; } /// /// /// /// /// /// private TextBlock CreateMarkerContent(object sender, Point positionChangedArgs) { var info = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart?.HitTest(positionChangedArgs); if (info?.X == null) return null; var pointIndex = info.PointIndex; var x = GraphDataSeries[0].Xvalue.FirstOrDefault(xvalue => xvalue.Equals(info.X)); var p1 = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.PointToData(positionChangedArgs); var x1 = GraphDataSeries[0].Xvalue.FirstOrDefault(xvalue => xvalue.Equals(p1.X)); var tb = new TextBlock(); tb.Inlines.Add(new Run { //TODO: Need to test correct X value [AF] Text = "Time: " + info.X, }); tb.Name = ((C1LineMarker)sender).Name + "Marker"; tb.Text = "Time: " + info.X; tb.Tag = (Point)info.Item; foreach (var series in ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Series) { var value = series.GetValues(0)[pointIndex]; var content = $"\n{series.SeriesName} = {value.ToString(CultureInfo.InvariantCulture)}"; tb.Inlines.Add(new Run() { Text = content, Foreground = new SolidColorBrush() { Color = ((SolidColorBrush)series.Style.Fill).Color } }); } tb.Visibility = Visibility.Visible; return tb; } //private TextBlock CreateMarkerContent(object sender, int index) //{ // var info = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart?.HitTest(positionChangedArgs); // if (info?.X == null) return null; // var pointIndex = info.PointIndex; // var tb = new TextBlock(); // tb.Inlines.Add(new Run // { // //TODO: Need to test correct X value [AF] // Text = "Time: " + info.X, // }); // tb.Name = ((C1LineMarker)sender).Name + "Marker"; // tb.Text = "Time: " + info.X; // tb.Tag = (Point)info.Item; // foreach (var series in ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Series) // { // var value = series.GetValues(0)[pointIndex]; // var content = $"\n{series.SeriesName} = {value.ToString(CultureInfo.InvariantCulture)}"; // tb.Inlines.Add(new Run() // { // Text = content, // Foreground = new SolidColorBrush() { Color = ((SolidColorBrush)series.Style.Fill).Color } // }); // } // tb.Visibility = Visibility.Visible; // return tb; //} //private void LineMarker_PositionChanged(object sender, PositionChangedArgs e) //{ // if (((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart != null) // { // var info = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.HitTest(new Point(e.Position.X, double.NaN)); // var pointIndex = info.PointIndex; // var tb = new TextBlock(); // tb.Inlines.Add(new Run() // { // Text = info.X.ToString() // }); // for (int index = 0; index < ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Series.Count; index++) // { // var series = ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Series[index]; // var value = series.GetValues(0)[pointIndex]; // var fill = (int)((C1.Chart.IChart)((C1LineMarker)sender)).GetColor(index); // var content = $"{"\n"}{series.SeriesName} = {string.Format("{0:f2}", value)}"; // tb.Inlines.Add(new Run() // { // Text = content, // Foreground = new SolidColorBrush() { Color = FromArgb(fill) } // }); // } // ((C1LineMarker)sender).Content = tb; // } //} //Color FromArgb(int clr) //{ // var bytes = BitConverter.GetBytes(clr); // return Color.FromArgb(bytes[3], bytes[2], bytes[1], bytes[0]); //} //private C1LineMarker SelectedMarker = null; private C1LineMarker FindMarker() { C1LineMarker marker = null; foreach (var layer in ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers) { if (layer.GetType() != typeof(C1LineMarker)) continue; if (((C1LineMarker)layer).BorderThickness.Left == 1) { marker = (C1LineMarker)layer; } } return marker; } private C1LineMarker FindMarker(Point point) { C1LineMarker marker = null; foreach (var layer in ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers) { if (layer.GetType() != typeof(C1LineMarker)) continue; if (((C1LineMarker)layer).Margin.Left >= point.X - 2 && ((C1LineMarker)layer).Margin.Left <= point.X + 2) { marker = (C1LineMarker)layer; } } return marker; //return ((TestDataView) ((IGraphViewModel) Parent).DataSeriesView).MainChart.Layers.Where(layer => layer.GetType() == typeof(C1LineMarker)).Where(layer => ((C1LineMarker) layer).X >= point.X - 2 && ((C1LineMarker) layer).X <= point.X + 2).Cast().FirstOrDefault(); } private void LineMarker_LostFocus(object sender, RoutedEventArgs e) { ((C1LineMarker)sender).BorderThickness = new Thickness(0, 0, 0, 0); } private void LineMarker_GotFocus(object sender, RoutedEventArgs e) { ((C1LineMarker)sender).BorderBrush = new SolidColorBrush(Colors.Red); ((C1LineMarker)sender).BorderThickness = new Thickness(1, 1, 1, 1); } private void LineMarker_MouseDown(object sender, MouseButtonEventArgs e) { ((C1LineMarker)sender).Focus(); } private void SetAxisTitle(IChartOptionsModel args) { switch (args.UnitType) { case ChartUnitTypeEnum.ADC: TitleY = "A/D Counts"; break; case ChartUnitTypeEnum.EU: TitleY = "Scaled EU"; break; case ChartUnitTypeEnum.mV: TitleY = "mV"; break; } CreateTitle("y", TitleY); CreateTitle("x", "ms"); } public void UpdateScrollbars() { ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisX.Scrollbar.Visibility = ChartViewOptions.YRange == YRangeScaleEnum.AutoRange ? Visibility.Collapsed : Visibility.Visible; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Scrollbar.Visibility = ChartViewOptions.YRange == YRangeScaleEnum.AutoRange ? Visibility.Collapsed : Visibility.Visible; //((AxisScrollBar) view.MainChart.View.AxisX.ScrollBar).Visibility = view.MainChart.View.AxisX.Scale >= 1.0 ? Visibility.Collapsed : Visibility.Visible; //((AxisScrollBar) view.MainChart.View.AxisY.ScrollBar).Visibility = view.MainChart.View.AxisY.Scale >= 1.0 ? Visibility.Collapsed : Visibility.Visible; } public void CreateTitle(string axis, string text) { var view = (TestDataView)((IGraphViewModel)Parent).DataSeriesView; if (axis == "x") { view.MainChart.AxisX.Title = text; } if (axis == "y") { view.MainChart.AxisY.Title = text; } } public void CreateTitle(Axis ax, string text, SolidColorBrush fg) { var tb = new TextBlock() { TextAlignment = TextAlignment.Center, Text = text, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Bottom, Width = 400, RenderTransformOrigin = new Point(0.5, 0.5) // new Point(0.8,1.1), }; if (fg != null) tb.Foreground = fg; ax.Title = new Border() { HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top, Child = tb, }; } public void ResetZoom() { ChartViewOptions.CanPublishChanges = false; ChartViewOptions.YRange = YRangeScaleEnum.AutoRange; ChartViewOptions.MaxFixedT = Math.Max(GraphDataSeries.Max(x => x.Xvalue.Max()), ChartViewOptions.MaxFixedT); ChartViewOptions.MinFixedT = Math.Min(GraphDataSeries.Min(x => x.Xvalue.Min()), ChartViewOptions.MinFixedT); var view = (TestDataView)((IGraphViewModel)Parent).DataSeriesView; ChartViewOptions.LockedT = false; ChartViewOptions.SelectedFullScaleValue = 100; view.MainChart.AxisX.Max = ChartViewOptions.MaxFixedT; view.MainChart.AxisX.Min = ChartViewOptions.MinFixedT; /* view.MainChart.AxisX.Min = double.NaN; view.MainChart.AxisX.Max = double.NaN; view.MainChart.AxisY.Min = double.NaN; view.MainChart.AxisY.Max = double.NaN; */ ReSetScale(); UpdateScrollbars(); ApplyChartOptions(ChartViewOptions); //ReDrawMrkers(); ChartViewOptions.CanPublishChanges = true; } public void ResetT() { ChartViewOptions.CanPublishChanges = false; ChartViewOptions.MaxFixedT = Math.Max(TestChannelList.Max(x => x.Xmax), ChartViewOptions.MaxFixedT); ChartViewOptions.MinFixedT = Math.Min(TestChannelList.Min(x => x.Xmin), ChartViewOptions.MinFixedT); ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisX.Max = ChartViewOptions.MaxFixedT; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisX.Min = ChartViewOptions.MinFixedT; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Max = CurrentYMax; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Min = CurrentYMin; ChartViewOptions.LockedT = false; ReSetScale(); UpdateScrollbars(); //ReDrawMrkers(); ChartViewOptions.CanPublishChanges = true; } private void ReSetScale() { var view = (TestDataView)((IGraphViewModel)Parent).DataSeriesView; //view.MainChart.AxisX.Scale = 1; //view.MainChart.AxisX.Value = 0.5; //view.MainChart.AxisY.Scale = 1; //view.MainChart.AxisY.Value = 0.5; } public void ScaleChart() { //var view = (TestDataView)((IGraphViewModel)Parent).DataSeriesView; switch (ChartViewOptions.YRange) { case YRangeScaleEnum.Fixed: CanPublishChanges = false; CurrentYMax = ChartViewOptions.MaxFixedY; CurrentYMin = ChartViewOptions.MinFixedY; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Max = CurrentYMax; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Min = CurrentYMin; CanPublishChanges = true; break; case YRangeScaleEnum.FullScale: CurrentYMax = double.MinValue; CurrentYMin = double.MaxValue; foreach (var channel in TestChannelList) { CalculateMinMax(channel, ChartViewOptions); } ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Max = CurrentYMax; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Min = CurrentYMin; break; case YRangeScaleEnum.AutoRange: CurrentYMax = double.MinValue; CurrentYMin = double.MaxValue; foreach (var channel in TestChannelList) { CalculateMinMax(channel, ChartViewOptions); } //view.MainChart.AxisY.AutoMin = false; //view.MainChart.AxisY.AutoMax = false; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Max = CurrentYMax; ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.AxisY.Min = CurrentYMin; //view.MainChart.AxisY.Scale = 1; break; } } private void CalculateMinMax(ITestChannel channel, IChartOptionsModel chartOptions) { switch (chartOptions.YRange) { case YRangeScaleEnum.Fixed: case YRangeScaleEnum.FullScale: switch (chartOptions.UnitType) { case ChartUnitTypeEnum.ADC: CurrentYMax = Math.Max(channel.ActualMaxRangeAdc * CalculateScalingFactor(channel, chartOptions), CurrentYMax); CurrentYMin = Math.Min(channel.ActualMinRangeAdc * CalculateScalingFactor(channel, chartOptions), CurrentYMin); break; case ChartUnitTypeEnum.EU: if (channel.ProportionalToExcitation || channel.SensorCapacity == 0) { CurrentYMax = Math.Max(CalculateFixedRange(channel, chartOptions, channel.DesiredRange), CurrentYMax); CurrentYMin = Math.Min(CalculateFixedRange(channel, chartOptions, -channel.DesiredRange), CurrentYMin); } else { CurrentYMax = Math.Max(CalculateFixedRange(channel, chartOptions, channel.SensorCapacity), CurrentYMax); CurrentYMin = Math.Min(CalculateFixedRange(channel, chartOptions, -channel.SensorCapacity), CurrentYMin); } break; case ChartUnitTypeEnum.mV: CurrentYMax = Math.Max(channel.ActualMaxRangeMv * CalculateScalingFactor(channel, chartOptions), CurrentYMax); CurrentYMin = Math.Min(channel.ActualMinRangeMv * CalculateScalingFactor(channel, chartOptions), CurrentYMax); break; } break; case YRangeScaleEnum.AutoRange: var minY = GraphDataSeries.Where(ch => ch.TestId == channel.TestId && ch.ChannelId == channel.ChannelId).Min(x => x.Yvalue.Min()); var maxY = GraphDataSeries.Where(ch => ch.TestId == channel.TestId && ch.ChannelId == channel.ChannelId).Max(x => x.Yvalue.Max()); switch (chartOptions.UnitType) { case ChartUnitTypeEnum.ADC: CalculateMinMaxAutoScaling(channel.ActualMaxRangeAdc, minY, maxY); break; case ChartUnitTypeEnum.EU: CalculateMinMaxAutoScaling(channel.ActualMaxRangeEu, minY, maxY); break; case ChartUnitTypeEnum.mV: CalculateMinMaxAutoScaling(channel.ActualMaxRangeMv, minY, maxY); break; } break; } } private const double MIN_AUTOZOOM = 0.1D; private const double MAX_AUTOZOOM = 1.2D; /// /// Copied from DataPro /// /// /// /// /// /// per http://fogbugz/fogbugz/default.asp?8281 use 120% /// The idea here is to have a moving window no smaller than +/-30% of capacity. /// Also make sure that the top of the signal is seen so make the visible window /// +/-120% of the signal /// public void CalculateMinMaxAutoScaling(double capacity, double dataMin, double dataMax) { capacity = Math.Abs(capacity); var middleOfSignalExtrema = ((dataMax - dataMin) / 2.0D) + dataMin; var minZoom = capacity * MIN_AUTOZOOM; var topOfMinZoomWindow = middleOfSignalExtrema + minZoom; var bottomOfMinZoomWindow = middleOfSignalExtrema - minZoom; CurrentYMax = (dataMax * MAX_AUTOZOOM >= topOfMinZoomWindow) ? dataMax * MAX_AUTOZOOM : topOfMinZoomWindow; CurrentYMin = (dataMin * MAX_AUTOZOOM <= bottomOfMinZoomWindow) ? dataMin * MAX_AUTOZOOM : bottomOfMinZoomWindow; } public double FixedRangePercentageOfFullScale = 100; public double FixedRangeValue = 5000; private double CalculateScalingFactor(ITestChannel channel, IChartOptionsModel chartOptions) { switch (chartOptions.YRange) { case YRangeScaleEnum.FullScale: return chartOptions.SelectedFullScaleValue / 100.0; case YRangeScaleEnum.Fixed: return channel.ActualMaxRangeEu == 0.0 ? 1.0 : FixedRangeValue / channel.ActualMaxRangeEu; } return 0.00; } /// /// Calculate Fixed Range /// /// /// /// /// private double CalculateFixedRange(ITestChannel channel, IChartOptionsModel chartOptions, double range) { switch (chartOptions.YRange) { case YRangeScaleEnum.FullScale: return range * (chartOptions.SelectedFullScaleValue / 100.0); case YRangeScaleEnum.Fixed: return range == 0.0 ? 1.0 : Math.Abs(FixedRangeValue / range); } return 0.00; } #endregion #region Commands private DelegateCommand _actionLeaveCommand; public DelegateCommand ActionLeaveCommand => _actionLeaveCommand ?? (_actionLeaveCommand = new DelegateCommand(ActionLeaveMethod)); /// /// Zoom end function /// /// Ignore X if Locked T /// private void ActionLeaveMethod(object sender) { ChartCursor = Cursors.Arrow; //var view = (TestDataView)((IGraphViewModel)Parent).DataSeriesView; //if (ChartOptions.LockedT) //{ // view.MainChart.View.AxisX.Max = ChartOptions.MaxFixedT; // view.MainChart.View.AxisX.Min = ChartOptions.MinFixedT; // view.MainChart.View.AxisX.Scale = 1; //} //else //{ // CurrentXMax = view.MainChart.View.AxisX.GetDispMax(); // CurrentXMin = view.MainChart.View.AxisX.GetDispMin(); //} //CurrentYMax = view.MainChart.View.AxisY.GetDispMax(); //CurrentYMin = view.MainChart.View.AxisY.GetDispMin(); //ChartOptions.CanPublishChanges = false; //ChartOptions.YRange = YRangeScaleEnum.Manual; //ChartOptions.CanPublishChanges = true; ////ScaleChart(); //UpdateScrollbars(); } private DelegateCommand _gotFocusCommand; public DelegateCommand GotFocusCommand => _gotFocusCommand ?? (_gotFocusCommand = new DelegateCommand(GotFocusCommandMethod)); private void GotFocusCommandMethod(object sender) { } private DelegateCommand _createMarkerCommand; public DelegateCommand CreateMarkerCommand => _createMarkerCommand ?? (_createMarkerCommand = new DelegateCommand(CreateMarkerMethod)); private void CreateMarkerMethod(object sender) { if (ChartViewOptions.ShowCursor) { ((TestDataView)((IGraphViewModel)Parent).DataSeriesView).MainChart.Layers.Add(CreateMarker(_currentPoint)); //SetContentAlignment(); _eventAggregator.GetEvent().Publish(true); } } #endregion Commands } }