710 lines
25 KiB
C#
710 lines
25 KiB
C#
|
/************************************************************************
|
|||
|
*@file FrameworkLocal\UIClient\CenterViews\Core\UserControls\DataViewDataGrid.cs
|
|||
|
* @author Su Liang
|
|||
|
* @Date 2022-08-01
|
|||
|
*
|
|||
|
* @copyright © Sicentury Inc.
|
|||
|
*
|
|||
|
* @brief Reconstructed to support rich functions.
|
|||
|
*
|
|||
|
* @details
|
|||
|
* *****************************************************************************/
|
|||
|
|
|||
|
using System;
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Collections.Specialized;
|
|||
|
using System.ComponentModel;
|
|||
|
using System.Diagnostics;
|
|||
|
using System.IO;
|
|||
|
using System.Linq;
|
|||
|
using System.Text;
|
|||
|
using System.Threading;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using System.Windows;
|
|||
|
using System.Windows.Controls;
|
|||
|
using System.Windows.Forms;
|
|||
|
using System.Windows.Input;
|
|||
|
using MECF.Framework.UI.Client.CenterViews.Core.Charting;
|
|||
|
using MECF.Framework.UI.Client.CenterViews.Core.EventArgs;
|
|||
|
using SciChart.Charting.Visuals.RenderableSeries;
|
|||
|
using Sicentury.Core.EventArgs;
|
|||
|
using MessageBox = System.Windows.MessageBox;
|
|||
|
|
|||
|
namespace MECF.Framework.UI.Client.CenterViews.Core.UserControls
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Interaction logic for DataViewDataGrid.xaml
|
|||
|
/// </summary>
|
|||
|
public partial class DataViewDataGrid
|
|||
|
{
|
|||
|
|
|||
|
#region Variables
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 正在导出数据事件。
|
|||
|
/// </summary>
|
|||
|
public event EventHandler Exporting;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 导出数据完毕事件。
|
|||
|
/// <para>导出过程异常仍触发此事件。</para>
|
|||
|
/// </summary>
|
|||
|
public event EventHandler Exported;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 正在删除曲线事件。
|
|||
|
/// </summary>
|
|||
|
public event EventHandler<RenderableSeriesDeletingEventArgs> Deleting;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 删除曲线完成事件。
|
|||
|
/// </summary>
|
|||
|
public event EventHandler<RenderableSeriesDeletingEventArgs> Deleted;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 常时操作的进度信息更新事件。
|
|||
|
/// </summary>
|
|||
|
public event EventHandler<ProgressUpdatingEventArgs> ProgressMessageUpdating;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 取消操作。
|
|||
|
/// </summary>
|
|||
|
private CancellationTokenSource _cancellationTokenSource;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 在UI线程上报告进度。
|
|||
|
/// </summary>
|
|||
|
private readonly IProgress<ProgressUpdatingEventArgs> _progress;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Constructors
|
|||
|
|
|||
|
public DataViewDataGrid()
|
|||
|
{
|
|||
|
_progress = new Progress<ProgressUpdatingEventArgs>(e => { ProgressMessageUpdating?.Invoke(this, e); });
|
|||
|
}
|
|||
|
|
|||
|
private void ItemsSourceOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|||
|
{
|
|||
|
switch (e.Action)
|
|||
|
{
|
|||
|
case NotifyCollectionChangedAction.Add:
|
|||
|
RegisterPropertiesChangedEvent(e.NewItems);
|
|||
|
break;
|
|||
|
|
|||
|
case NotifyCollectionChangedAction.Replace:
|
|||
|
RegisterPropertiesChangedEvent(e.NewItems);
|
|||
|
UnRegisterPropertiesChangedEvent(e.OldItems);
|
|||
|
break;
|
|||
|
|
|||
|
case NotifyCollectionChangedAction.Remove:
|
|||
|
UnRegisterPropertiesChangedEvent(e.OldItems);
|
|||
|
break;
|
|||
|
|
|||
|
case NotifyCollectionChangedAction.Reset:
|
|||
|
UnRegisterPropertiesChangedEvent(e.OldItems);
|
|||
|
RegisterPropertiesChangedEvent(e.NewItems);
|
|||
|
break;
|
|||
|
|
|||
|
case NotifyCollectionChangedAction.Move:
|
|||
|
default:
|
|||
|
// Ignore.
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void RegisterPropertiesChangedEvent(IList collection)
|
|||
|
{
|
|||
|
foreach (var item in collection)
|
|||
|
{
|
|||
|
if (item is SicFastLineSeries series)
|
|||
|
{
|
|||
|
series.PropertyChanged += SeriesOnPropertyChanged;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void UnRegisterPropertiesChangedEvent(IList collection)
|
|||
|
{
|
|||
|
foreach (var item in collection)
|
|||
|
{
|
|||
|
if (item is SicFastLineSeries series)
|
|||
|
{
|
|||
|
series.PropertyChanged -= SeriesOnPropertyChanged;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void SeriesOnPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|||
|
{
|
|||
|
if (e.PropertyName == nameof(IRenderableSeries.IsVisible))
|
|||
|
{
|
|||
|
// 设置标题栏中CheckBox的IsChecked属性
|
|||
|
var group =
|
|||
|
ItemsSource.ToList()
|
|||
|
.Cast<SicFastLineSeries>()
|
|||
|
.GroupBy(x => x.IsVisible)
|
|||
|
.ToList();
|
|||
|
|
|||
|
if (group.Count() > 1)
|
|||
|
{
|
|||
|
IsVisibleInColumnHeader = null;
|
|||
|
}
|
|||
|
else if (group.Count() == 1)
|
|||
|
{
|
|||
|
var visible = group[0].Key;
|
|||
|
IsVisibleInColumnHeader = visible;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Properties
|
|||
|
|
|||
|
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
|
|||
|
"ItemsSource", typeof(ChartingLineSeriesCollection), typeof(DataViewDataGrid),
|
|||
|
new PropertyMetadata(default(ChartingLineSeriesCollection), (sender, e) =>
|
|||
|
{
|
|||
|
//if (!(sender is DataViewDataGrid dg))
|
|||
|
// return;
|
|||
|
|
|||
|
//if (e.NewValue is ChartingLineSeriesCollection newColl)
|
|||
|
//{
|
|||
|
// newColl.CollectionChanged += dg.ItemsSourceOnCollectionChanged;
|
|||
|
// dg.RegisterPropertiesChangedEvent(newColl);
|
|||
|
//}
|
|||
|
|
|||
|
//if (e.OldValue is ChartingLineSeriesCollection oldColl)
|
|||
|
//{
|
|||
|
// oldColl.CollectionChanged -= dg.ItemsSourceOnCollectionChanged;
|
|||
|
// dg.UnRegisterPropertiesChangedEvent(oldColl);
|
|||
|
//}
|
|||
|
|
|||
|
}));
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 设置或返回DataGrid数据源。
|
|||
|
/// </summary>
|
|||
|
public ChartingLineSeriesCollection ItemsSource
|
|||
|
{
|
|||
|
get => (ChartingLineSeriesCollection)GetValue(ItemsSourceProperty);
|
|||
|
set => SetValue(ItemsSourceProperty, value);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public static readonly DependencyProperty IsShowStatisticColumnProperty = DependencyProperty.Register(
|
|||
|
nameof(IsShowStatisticColumn),
|
|||
|
typeof(bool),
|
|||
|
typeof(DataViewDataGrid),
|
|||
|
new PropertyMetadata(default(bool)));
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 设置或返回是否显示统计数据列。
|
|||
|
/// </summary>
|
|||
|
public bool IsShowStatisticColumn
|
|||
|
{
|
|||
|
get => (bool)GetValue(IsShowStatisticColumnProperty);
|
|||
|
set => SetValue(IsShowStatisticColumnProperty, value);
|
|||
|
}
|
|||
|
|
|||
|
public static readonly DependencyProperty IsVisibleInColumnHeaderProperty = DependencyProperty.Register(
|
|||
|
"IsVisibleInColumnHeader",
|
|||
|
typeof(bool?),
|
|||
|
typeof(DataViewDataGrid),
|
|||
|
new PropertyMetadata(default(bool?), (sender, e) =>
|
|||
|
{
|
|||
|
if (!(sender is DataViewDataGrid dg))
|
|||
|
return;
|
|||
|
|
|||
|
if (e.Property.Name != nameof(IsVisibleInColumnHeader))
|
|||
|
return;
|
|||
|
|
|||
|
if (!(e.NewValue is bool visible))
|
|||
|
return;
|
|||
|
|
|||
|
// 如果CheckBox被选中状态,则本次点击后隐藏所有序列。
|
|||
|
|
|||
|
foreach (var series in dg.ItemsSource)
|
|||
|
{
|
|||
|
series.IsVisible = visible;
|
|||
|
}
|
|||
|
}));
|
|||
|
|
|||
|
public bool? IsVisibleInColumnHeader
|
|||
|
{
|
|||
|
get => (bool?)GetValue(IsVisibleInColumnHeaderProperty);
|
|||
|
set => SetValue(IsVisibleInColumnHeaderProperty, value);
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Methods
|
|||
|
|
|||
|
protected override void OnInitialized(System.EventArgs e)
|
|||
|
{
|
|||
|
InitializeComponent();
|
|||
|
base.OnInitialized(e);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 取消操作。
|
|||
|
/// </summary>
|
|||
|
public void CancelOperation()
|
|||
|
{
|
|||
|
if (_cancellationTokenSource?.Token.CanBeCanceled == true)
|
|||
|
_cancellationTokenSource.Cancel();
|
|||
|
}
|
|||
|
|
|||
|
private void OnProgressMessageUpdating(int currentProgress, int totalProgress, string message)
|
|||
|
{
|
|||
|
_progress.Report(new ProgressUpdatingEventArgs(currentProgress, totalProgress, message));
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Events
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 导出全部曲线数据。
|
|||
|
/// </summary>
|
|||
|
/// <param name="sender"></param>
|
|||
|
/// <param name="e"></param>
|
|||
|
private async void BtnExportAll_OnClick(object sender, RoutedEventArgs e)
|
|||
|
{
|
|||
|
if (!(ItemsSource is ChartingLineSeriesCollection collection))
|
|||
|
return;
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
if (collection.Count == 0)
|
|||
|
{
|
|||
|
MessageBox.Show($"Please select the data you want to export.", "Export", MessageBoxButton.OK,
|
|||
|
MessageBoxImage.Warning);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
#if EXPORT_TO_CSV
|
|||
|
var dlg = new SaveFileDialog
|
|||
|
{
|
|||
|
DefaultExt = ".xlsx", // Default file extension
|
|||
|
Filter = "Excel数据表格文件(*.csv)|*.csv", // Filter files by extension
|
|||
|
FileName = $"{collection.DisplayName}_{DateTime.Now:yyyyMMdd_HHmmss}"
|
|||
|
};
|
|||
|
#else
|
|||
|
var dlg = new SaveFileDialog
|
|||
|
{
|
|||
|
DefaultExt = ".xlsx", // Default file extension
|
|||
|
Filter = "Excel数据表格文件(*.xlsx)|*.xlsx", // Filter files by extension
|
|||
|
FileName = $"{DisplayName}_{DateTime.Now:yyyyMMdd_HHmmss}"
|
|||
|
};
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
var ret = dlg.ShowDialog(); // Show open file dialog box
|
|||
|
if (ret == DialogResult.OK) // Process open file dialog box results
|
|||
|
{
|
|||
|
Exporting?.Invoke(this, System.EventArgs.Empty);
|
|||
|
_cancellationTokenSource = new CancellationTokenSource();
|
|||
|
|
|||
|
var sw = new Stopwatch();
|
|||
|
sw.Restart();
|
|||
|
|
|||
|
#if EXPORT_TO_CSV
|
|||
|
var columns = new List<string>();
|
|||
|
#else
|
|||
|
var ds = new DataSet();
|
|||
|
ds.Tables.Add(new DataTable(dlg.FileName));
|
|||
|
ds.Tables[0].Columns.Add("Time");
|
|||
|
ds.Tables[0].Columns[0].DataType = typeof(DateTime);
|
|||
|
#endif
|
|||
|
|
|||
|
var timeValue = new Dictionary<DateTime, double[]>();
|
|||
|
var dataSeriesCollection =
|
|||
|
collection.Cast<SicFastLineSeries>().Select(x => x.GetDataSeries()).ToList();
|
|||
|
|
|||
|
await Task.Run(() =>
|
|||
|
{
|
|||
|
for (var i = 0; i < dataSeriesCollection.Count; i++)
|
|||
|
{
|
|||
|
|
|||
|
OnProgressMessageUpdating(
|
|||
|
50,
|
|||
|
100,
|
|||
|
$"Exporting {dataSeriesCollection[i].SeriesName} {i}/{dataSeriesCollection.Count} ...");
|
|||
|
|
|||
|
var points = dataSeriesCollection[i].Metadata
|
|||
|
.Cast<ParameterNodePoint>().ToList();
|
|||
|
|
|||
|
for (var n = 0; n < points.Count; n++)
|
|||
|
{
|
|||
|
var p = points[n];
|
|||
|
|
|||
|
if (!timeValue.ContainsKey(p.Time))
|
|||
|
timeValue[p.Time] = new double[collection.Count];
|
|||
|
|
|||
|
timeValue[p.Time][i] = p.Value;
|
|||
|
|
|||
|
if (_cancellationTokenSource.Token.IsCancellationRequested)
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
#if EXPORT_TO_CSV
|
|||
|
columns.Add((collection[i] as SicFastLineSeries)?.DataName ?? "Unknown Col");
|
|||
|
#else
|
|||
|
ds.Tables[0].Columns.Add((SelectedData[i] as SicFastLineSeries)?.DataName);
|
|||
|
ds.Tables[0].Columns[i + 1].DataType = typeof(double);
|
|||
|
#endif
|
|||
|
if (_cancellationTokenSource.Token.IsCancellationRequested)
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
}, _cancellationTokenSource.Token).ContinueWith(t =>
|
|||
|
{
|
|||
|
if (t.IsCanceled || t.IsFaulted)
|
|||
|
return;
|
|||
|
#if EXPORT_TO_CSV
|
|||
|
var csvBuilder = new StringBuilder();
|
|||
|
csvBuilder.Append("Time,");
|
|||
|
csvBuilder.AppendLine(string.Join(",", columns));
|
|||
|
|
|||
|
var totalPoints = (double)timeValue.Count;
|
|||
|
var processedPoints = 0d;
|
|||
|
var lastPercent = 0d;
|
|||
|
foreach (var tv in timeValue)
|
|||
|
{
|
|||
|
csvBuilder.Append($" {tv.Key:yyyy/MM/dd HH:mm:ss.fff}");
|
|||
|
csvBuilder.Append(",");
|
|||
|
csvBuilder.AppendLine(string.Join(",", tv.Value));
|
|||
|
|
|||
|
if (_cancellationTokenSource?.Token.IsCancellationRequested == true)
|
|||
|
return;
|
|||
|
|
|||
|
processedPoints++;
|
|||
|
var currentPercent = (int)(processedPoints / totalPoints * 100);
|
|||
|
if (currentPercent > lastPercent)
|
|||
|
{
|
|||
|
/*
|
|||
|
* 不要太频繁更新进度,否则会导致UI卡顿,每增长1%更新一次进度。
|
|||
|
* currentPercent和lastPercent必须是整数,以将进度更新的频率控制在要求内。
|
|||
|
*/
|
|||
|
OnProgressMessageUpdating(
|
|||
|
60,
|
|||
|
100,
|
|||
|
$"Building report content ({currentPercent}%) ...");
|
|||
|
lastPercent = currentPercent;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
OnProgressMessageUpdating(
|
|||
|
90,
|
|||
|
100,
|
|||
|
$"Writing to file ...");
|
|||
|
using (_cancellationTokenSource.Token.Register(Thread.CurrentThread.Abort))
|
|||
|
{
|
|||
|
File.WriteAllText(dlg.FileName, csvBuilder.ToString());
|
|||
|
}
|
|||
|
#else
|
|||
|
|
|||
|
OnProgressMessageUpdating(
|
|||
|
60,
|
|||
|
100,
|
|||
|
$"Building report content ...");
|
|||
|
|
|||
|
foreach (var item in timeValue)
|
|||
|
{
|
|||
|
var row = ds.Tables[0].NewRow();
|
|||
|
row[0] = item.Key;
|
|||
|
for (var j = 0; j < item.Value.Length; j++)
|
|||
|
{
|
|||
|
row[j + 1] = item.Value[j];
|
|||
|
}
|
|||
|
|
|||
|
ds.Tables[0].Rows.Add(row);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
OnProgressMessageUpdating(
|
|||
|
90,
|
|||
|
100,
|
|||
|
$"Writing to file ...");
|
|||
|
using (_cancellationTokenSource.Token.Register(Thread.CurrentThread.Abort))
|
|||
|
{
|
|||
|
if (!ExcelHelper.ExportToExcel(dlg.FileName, ds, out var reason))
|
|||
|
{
|
|||
|
MessageBox.Show($"Export failed, {reason}", "Export", MessageBoxButton.OK,
|
|||
|
MessageBoxImage.Warning);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
});
|
|||
|
|
|||
|
|
|||
|
sw.Stop();
|
|||
|
Debug.WriteLine($"Export costs {sw.ElapsedMilliseconds}ms");
|
|||
|
Debug.WriteLine($"Total Lines {timeValue.Count}");
|
|||
|
|
|||
|
if (_cancellationTokenSource?.Token.IsCancellationRequested == true)
|
|||
|
return;
|
|||
|
|
|||
|
Exported?.Invoke(this, System.EventArgs.Empty);
|
|||
|
|
|||
|
MessageBox.Show($"Exporting succeed, file save as {dlg.FileName}", "Export", MessageBoxButton.OK,
|
|||
|
MessageBoxImage.Information);
|
|||
|
}
|
|||
|
}
|
|||
|
catch (ThreadAbortException)
|
|||
|
{
|
|||
|
// 操作取消。
|
|||
|
}
|
|||
|
catch (AggregateException ae)
|
|||
|
{
|
|||
|
var ex = ae.Flatten().InnerExceptions;
|
|||
|
throw ex.FirstOrDefault() ?? ae;
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
MessageBox.Show($"Unable to export data, {ex.Message}", "Error", MessageBoxButton.OK,
|
|||
|
MessageBoxImage.Error);
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
Exported?.Invoke(this, System.EventArgs.Empty);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 导出单条曲线数据。
|
|||
|
/// </summary>
|
|||
|
/// <param name="sender"></param>
|
|||
|
/// <param name="e"></param>
|
|||
|
private async void BtnExport_OnPreviewMouseUp(object sender, MouseButtonEventArgs e)
|
|||
|
{
|
|||
|
if (!(ItemsSource is ChartingLineSeriesCollection collection))
|
|||
|
return;
|
|||
|
|
|||
|
if (!(sender is TextBlock btn))
|
|||
|
return;
|
|||
|
|
|||
|
if (!(btn.DataContext is SicFastLineSeries series))
|
|||
|
return;
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
|
|||
|
#if EXPORT_TO_CSV
|
|||
|
var dlg = new SaveFileDialog
|
|||
|
{
|
|||
|
DefaultExt = ".xlsx", // Default file extension
|
|||
|
Filter = "Excel数据表格文件(*.csv)|*.csv", // Filter files by extension
|
|||
|
FileName = $"{series.DisplayName}_{DateTime.Now:yyyyMMdd_HHmmss}"
|
|||
|
};
|
|||
|
#else
|
|||
|
var dlg = new SaveFileDialog
|
|||
|
{
|
|||
|
DefaultExt = ".xlsx", // Default file extension
|
|||
|
Filter = "Excel数据表格文件(*.xlsx)|*.xlsx", // Filter files by extension
|
|||
|
FileName = $"{DisplayName}_{DateTime.Now:yyyyMMdd_HHmmss}"
|
|||
|
};
|
|||
|
|
|||
|
#endif
|
|||
|
var result = dlg.ShowDialog(); // Show open file dialog box
|
|||
|
if (result == DialogResult.OK) // Process open file dialog box results
|
|||
|
{
|
|||
|
_cancellationTokenSource = new CancellationTokenSource();
|
|||
|
Exporting?.Invoke(this, System.EventArgs.Empty);
|
|||
|
|
|||
|
var sw = new Stopwatch();
|
|||
|
sw.Restart();
|
|||
|
|
|||
|
#if EXPORT_TO_CSV
|
|||
|
var columns = new List<string>();
|
|||
|
#else
|
|||
|
var ds = new DataSet();
|
|||
|
ds.Tables.Add(new DataTable(cp.DataName));
|
|||
|
ds.Tables[0].Columns.Add("Time");
|
|||
|
ds.Tables[0].Columns[0].DataType = typeof(DateTime);
|
|||
|
ds.Tables[0].Columns.Add(cp.DataName);
|
|||
|
ds.Tables[0].Columns[1].DataType = typeof(double);
|
|||
|
#endif
|
|||
|
|
|||
|
var ds = series?.GetDataSeries();
|
|||
|
var points = ds?.Metadata.Cast<ParameterNodePoint>().ToList();
|
|||
|
|
|||
|
if (points == null)
|
|||
|
{
|
|||
|
MessageBox.Show($"Unable to find meta points from the series {series.DataName}.", "Error",
|
|||
|
MessageBoxButton.OK,
|
|||
|
MessageBoxImage.Error);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
var csvBuilder = new StringBuilder();
|
|||
|
|
|||
|
await Task.Run(() =>
|
|||
|
{
|
|||
|
using (_cancellationTokenSource.Token.Register(Thread.CurrentThread.Abort))
|
|||
|
{
|
|||
|
#if EXPORT_TO_CSV
|
|||
|
csvBuilder.AppendLine($"Time,{ds.SeriesName}"); // table header
|
|||
|
#endif
|
|||
|
OnProgressMessageUpdating(
|
|||
|
50,
|
|||
|
100,
|
|||
|
$"Exporting data ...");
|
|||
|
|
|||
|
for (var i = 0; i < points.Count; i++)
|
|||
|
{
|
|||
|
var p = points[i];
|
|||
|
#if EXPORT_TO_CSV
|
|||
|
csvBuilder.AppendLine($" {p.Time:yyyy/MM/dd HH:mm:ss.fff},{p.Value}");
|
|||
|
#else
|
|||
|
var row = ds.Tables[0].NewRow();
|
|||
|
row[0] = p.Time;
|
|||
|
row[1] = p.Value;
|
|||
|
ds.Tables[0].Rows.Add(row);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
OnProgressMessageUpdating(
|
|||
|
90,
|
|||
|
100,
|
|||
|
$"Writing to file ...");
|
|||
|
#if EXPORT_TO_CSV
|
|||
|
|
|||
|
File.WriteAllText(dlg.FileName, csvBuilder.ToString());
|
|||
|
#else
|
|||
|
if (!ExcelHelper.ExportToExcel(dlg.FileName, ds, out var reason))
|
|||
|
{
|
|||
|
MessageBox.Show($"Export failed, {reason}", "Export", MessageBoxButton.OK,
|
|||
|
MessageBoxImage.Warning);
|
|||
|
return;
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
sw.Stop();
|
|||
|
Debug.WriteLine($"Export costs {sw.ElapsedMilliseconds}ms");
|
|||
|
Debug.WriteLine($"Total Lines {points?.Count}");
|
|||
|
|
|||
|
Exported?.Invoke(this, System.EventArgs.Empty);
|
|||
|
|
|||
|
MessageBox.Show($"Export succeed, file save as {dlg.FileName}", "Export", MessageBoxButton.OK,
|
|||
|
MessageBoxImage.Information);
|
|||
|
}
|
|||
|
}
|
|||
|
catch (ThreadAbortException)
|
|||
|
{
|
|||
|
// 操作取消。
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
MessageBox.Show($"Unable to export data, {ex.Message}", "Error", MessageBoxButton.OK,
|
|||
|
MessageBoxImage.Error);
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
Exported?.Invoke(this, System.EventArgs.Empty);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 删除全部曲线。
|
|||
|
/// </summary>
|
|||
|
/// <param name="sender"></param>
|
|||
|
/// <param name="e"></param>
|
|||
|
private void BtnDeleteAll_OnClick(object sender, RoutedEventArgs e)
|
|||
|
{
|
|||
|
if (!(ItemsSource is ChartingLineSeriesCollection collection))
|
|||
|
return;
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
var list = collection.Cast<SicFastLineSeries>().ToList();
|
|||
|
var args = new RenderableSeriesDeletingEventArgs(list);
|
|||
|
|
|||
|
Deleting?.Invoke(this, args);
|
|||
|
if (args.Cancel)
|
|||
|
return;
|
|||
|
|
|||
|
var total = collection.Count;
|
|||
|
for (var i = total - 1; i >= 0; i--)
|
|||
|
{
|
|||
|
list[i].BackendParameterNode.IsSelected = false;
|
|||
|
}
|
|||
|
|
|||
|
collection.Clear();
|
|||
|
|
|||
|
Deleted?.Invoke(this, args);
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
MessageBox.Show($"It's failed to delete all series, {ex.Message}", "Error", MessageBoxButton.OK,
|
|||
|
MessageBoxImage.Error);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 删除单条曲线。
|
|||
|
/// </summary>
|
|||
|
/// <param name="sender"></param>
|
|||
|
/// <param name="e"></param>
|
|||
|
private void BtnDelete_OnPreviewMouseUp(object sender, MouseButtonEventArgs e)
|
|||
|
{
|
|||
|
if (!(ItemsSource is ChartingLineSeriesCollection collection))
|
|||
|
return;
|
|||
|
|
|||
|
if (!(sender is TextBlock btn))
|
|||
|
return;
|
|||
|
|
|||
|
if (!(btn.DataContext is SicFastLineSeries series))
|
|||
|
return;
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
var args = new RenderableSeriesDeletingEventArgs(
|
|||
|
new List<SicFastLineSeries>(new[] { series }));
|
|||
|
|
|||
|
Deleting?.Invoke(this, args);
|
|||
|
if (args.Cancel)
|
|||
|
return;
|
|||
|
|
|||
|
series.BackendParameterNode.IsSelected = false;
|
|||
|
collection.Remove(series);
|
|||
|
|
|||
|
Deleted?.Invoke(this, args);
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
MessageBox.Show($"It's failed to delete series, {ex.Message}", "Error", MessageBoxButton.OK,
|
|||
|
MessageBoxImage.Error);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 更换曲线颜色。
|
|||
|
/// </summary>
|
|||
|
/// <param name="sender"></param>
|
|||
|
/// <param name="e"></param>
|
|||
|
private void BtnChangeSeriesColor_OnPreviewMouseUp(object sender, MouseButtonEventArgs e)
|
|||
|
{
|
|||
|
var dlg = new System.Windows.Forms.ColorDialog();
|
|||
|
if (dlg.ShowDialog() != System.Windows.Forms.DialogResult.OK)
|
|||
|
return;
|
|||
|
|
|||
|
if (!(sender is Border bdr))
|
|||
|
return;
|
|||
|
|
|||
|
if (bdr.DataContext is SicFastLineSeries series)
|
|||
|
series.Stroke = new System.Windows.Media.Color()
|
|||
|
{ A = dlg.Color.A, B = dlg.Color.B, G = dlg.Color.G, R = dlg.Color.R };
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|