using Microsoft.VisualBasic.CompilerServices; using MuchInfo.Chart.Data.EnumTypes; using MuchInfo.Chart.Data.Interfaces; using MuchInfo.Chart.Data.Models; using MuchInfo.Chart.FormulaEdit; using MuchInfo.Chart.Infrastructure.Data; using MuchInfo.Chart.Infrastructure.Helpers; using MuchInfo.Chart.Infrastructure.Utilities; using MuchInfo.Chart.WPF.Controls.Drawing; using MuchInfo.Chart.WPF.Controls.QuickTimeSpan; using MuchInfo.Chart.WPF.Controls.ValueScaling; using MuchInfo.Chart.WPF.Helpers; using MuchInfo.Chart.WPF.Primitives; using MuchInfo.Chart.WPF.Primitives.Drawing; using MuchInfo.Chart.WPF.Primitives.Interfaces; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Threading; using Xceed.Wpf.Toolkit; namespace MuchInfo.Chart.WPF { #region Delegates /// /// 通过CycleType加载历史周期数据 /// /// 交易所代码 /// 商品代码 /// 周期 /// 起始时间 /// 结束时间 /// 数据前N条 /// List{ILineDataPoint}. public delegate List LoadHistoryCycleDataByCycleTypeEventHandler(int exchangeCode, string goodsCode, CycleType type, TimeSpan timeSpan, DateTime startTime, DateTime endTime, int topN); #endregion Delegates /// /// WPF图表控件 /// public class Chart : Control { #region 依赖属性 /// /// 左右键增减周期值 /// public static readonly DependencyProperty LeftRightKeyToAccelerationProperty = DependencyProperty.Register("LeftRightKeyToAcceleration", typeof(int), typeof(Chart), new PropertyMetadata(10)); /// /// 鼠标滚轮加速缩放因数 /// public static readonly DependencyProperty AcceleratedWheelFactorProperty = DependencyProperty.Register("AcceleratedWheelFactor", typeof(int), typeof(Chart), new PropertyMetadata(10)); /// /// 鼠标滚轮加速缩放键 /// public static readonly DependencyProperty AcceleratedWheelModifierKeysProperty = DependencyProperty.Register("AcceleratedWheelModifierKeys", typeof(ModifierKeys), typeof(Chart), new PropertyMetadata(ModifierKeys.Control)); /// /// 背景色 /// public static readonly DependencyProperty ChartBackgroundProperty = DependencyProperty.Register("ChartBackground", typeof(SolidColorBrush), typeof(Chart), new PropertyMetadata(new SolidColorBrush(Colors.Black), new PropertyChangedCallback(Chart.ChartFontSizeProperty_Changed))); /// /// 字体大小 /// public static readonly DependencyProperty ChartFontSizeProperty = DependencyProperty.Register("ChartFontSize", typeof(double), typeof(Chart), new PropertyMetadata(12d, new PropertyChangedCallback(Chart.ChartFontSizeProperty_Changed))); /// /// 绘图类型 /// public static readonly DependencyProperty ChartLanguageProperty = DependencyProperty.Register("ChartLanguage", typeof(ChartLanguageType), typeof(Chart), new PropertyMetadata(ChartLanguageType.SimplifiedChinese, new PropertyChangedCallback(Chart.ChartLanguageProperty_Changed))); /// /// The chart time span property /// public static readonly DependencyProperty ChartTimeSpanProperty = DependencyProperty.Register("ChartTimeSpan", typeof(TimeSpan), typeof(Chart), new PropertyMetadata(default(TimeSpan), Chart.ChartTimeSpanProperty_Changed)); /// /// The current goods property /// public static readonly DependencyProperty CurrentGoodsProperty = DependencyProperty.Register("CurrentGoods", typeof(GoodsInfo), typeof(Chart), new PropertyMetadata(null, Chart.CurrentGoodsProperty_Changed)); /// /// 商品周期 /// public static readonly DependencyProperty CycleTypeProperty = DependencyProperty.Register("CycleType", typeof(CycleType), typeof(Chart), new PropertyMetadata(CycleType.Minute, new PropertyChangedCallback(Chart.ChartCycleTypeProperty_Changed))); /// /// 商品周期集合 /// public static readonly DependencyProperty CycleItemsProperty = DependencyProperty.Register("CycleItems", typeof(List), typeof(Chart), new PropertyMetadata(new List(), new PropertyChangedCallback(Chart.ChartCycleTypeItemsProperty_Changed))); /// /// 当前Tik数据,只需更新Tik数据即可,控件自动累加 /// public BarDataPoint CurrentTik { get { return (BarDataPoint)GetValue(CurrentTikProperty); } set { SetValue(CurrentTikProperty, value); } } public static readonly DependencyProperty CurrentTikProperty = DependencyProperty.Register("CurrentTik", typeof(BarDataPoint), typeof(Chart), new PropertyMetadata(null, new PropertyChangedCallback(Chart.ChartTikProperty_Changed))); /// /// 画线工具位置 /// public static readonly DependencyProperty DrawingToolPositionProperty = DependencyProperty.Register("DrawingToolPosition", typeof(DrawingToolPositionType), typeof(Chart), new PropertyMetadata(DrawingToolPositionType.Left, new PropertyChangedCallback(Chart.DrawingToolPositionProperty_Changed))); /// /// 当前画线工具:可通过设置值来模拟画线工具单击事件 /// public static readonly DependencyProperty DrawingToolTypeProperty = DependencyProperty.Register("DrawingToolType", typeof(DrawingToolType), typeof(Chart), new PropertyMetadata(DrawingToolType.Cross, new PropertyChangedCallback(Chart.DrawingToolType_Changed))); /// /// 蜡烛图叠加是否填充 /// public static readonly DependencyProperty FillComparisonCandleProperty = DependencyProperty.Register("FillComparisonCandle", typeof(bool), typeof(Chart), new PropertyMetadata(false, Chart.ChartProperty_Changed)); /// /// 网格透明度 /// public static readonly DependencyProperty GridOpacityProperty = DependencyProperty.Register("GridOpacity", typeof(double), typeof(Chart), new PropertyMetadata(0.15d, new PropertyChangedCallback(Chart.ChartProperty_Changed))); /// /// 绘图类型 /// public static readonly DependencyProperty LinePlotTypeProperty = DependencyProperty.Register("LinePlotType", typeof(LinePlotType), typeof(Chart), new PropertyMetadata(LinePlotType.Candlestick, new PropertyChangedCallback(Chart.ChartLineTypePlotProperty_Changed))); /// /// 最大加载数据条数 /// public static readonly DependencyProperty MaxLoadRecordCountProperty = DependencyProperty.Register("MaxLoadRecordCount", typeof(int), typeof(Chart), new PropertyMetadata(500)); /// /// 最大显示数据条数 /// public static readonly DependencyProperty MaxRecordCountProperty = DependencyProperty.Register( "MaxRecordCount", typeof(int), typeof(Chart), new PropertyMetadata(1000, new PropertyChangedCallback(Chart.ChartMinMaxRecordProperty_Changed))); /// /// 最小显示数据条数 /// public static readonly DependencyProperty MinRecordCountProperty = DependencyProperty.Register( "MinRecordCount", typeof(int), typeof(Chart), new PropertyMetadata(50, new PropertyChangedCallback(Chart.ChartMinMaxRecordProperty_Changed))); /// /// 显示数据条数 /// public static readonly DependencyProperty RecordCountProperty = DependencyProperty.Register("RecordCount", typeof(int), typeof(Chart), new PropertyMetadata(100, new PropertyChangedCallback(Chart.ChartRecordCountProperty_Changed))); /// /// 是否显示画线工具 /// public static readonly DependencyProperty ShowDrawingToolProperty = DependencyProperty.Register("ShowDrawingTool", typeof(Visibility), typeof(Chart), new PropertyMetadata(Visibility.Visible, new PropertyChangedCallback(ElementInvalidator.PropertyChanged_InvalidateMeasureArrange))); /// /// 画图工具是否可用 /// public bool DrawingToolIsEnabled { get { return (bool)GetValue(DrawingToolIsEnabledProperty); } set { SetValue(DrawingToolIsEnabledProperty, value); } } public static readonly DependencyProperty DrawingToolIsEnabledProperty = DependencyProperty.Register("DrawingToolIsEnabled", typeof(bool), typeof(Chart), new PropertyMetadata(true)); /// /// 最多可显示多少个ChartPane /// public int MaxChartPaneCount { get { return (int)GetValue(MaxChartPaneCountProperty); } set { SetValue(MaxChartPaneCountProperty, value); } } public static readonly DependencyProperty MaxChartPaneCountProperty = DependencyProperty.Register("MaxChartPaneCount", typeof(int), typeof(Chart), new PropertyMetadata(5)); /// /// 设置分时图网格与刻度的坐标 /// public Brush TimeSharingGridBrush { get { return (Brush)GetValue(TimeSharingGridBrushProperty); } set { SetValue(TimeSharingGridBrushProperty, value); } } public static readonly DependencyProperty TimeSharingGridBrushProperty = DependencyProperty.Register("TimeSharingGridBrush", typeof(Brush), typeof(Chart), new PropertyMetadata(Brushes.Red)); /// /// 设置合适的边距缓冲区 /// public int RightBuffer { get { return (int)GetValue(RightBufferProperty); } set { SetValue(RightBufferProperty, value); } } public static readonly DependencyProperty RightBufferProperty = DependencyProperty.Register("RightBuffer", typeof(int), typeof(Chart), new PropertyMetadata(20)); public Visibility CycleBarVisibility { get { return (Visibility)GetValue(CycleBarVisibilityProperty); } set { SetValue(CycleBarVisibilityProperty, value); } } // Using a DependencyProperty as the backing store for CycleBarVisibility. This enables animation, styling, binding, etc... public static readonly DependencyProperty CycleBarVisibilityProperty = DependencyProperty.Register("CycleBarVisibility", typeof(Visibility), typeof(Chart), new PropertyMetadata(Visibility.Visible, new PropertyChangedCallback(Chart.ChartCycleBarVisibity))); /// /// 加载历史周期数据 By CycleType /// public LoadHistoryCycleDataByCycleTypeEventHandler LoadHistoryCycleDataByCycleType; /// /// drawLINE /// private static IDrawingTool _oldDrawingTool; //头容器 private StackPanel _chartHeaderStackPanel; //chart pane容器 private Grid _chartPanesGrid; private ChartToolbarExpander _chartToolbarExpander; private List _comparisonGoodsList; private DateTime _crossDateTime; /// /// 十字光标鼠标按下状态 /// private bool _crossFlag; //十字光标容器 private Canvas _crossHairGrid; private Path _crossPath; private Line _hline = new Line(); //横线 private Line _vline = new Line(); //竖线 /// /// 十字光标面板x坐标 /// private double _crossX; private IDrawingTool _currentDrawingTool; private double _currentX; private double _currentY; //数据滑块容器 private StackPanel _dateChartSliderStackPanel; private DateScaleDisplay _dateScaleDisplay; private Button _downloadMoreButton; private TextBlock _downloadMoreButtonPlaceHolder; private ChartDrawingToolbar _drawingToolbar; //画线工具容器 private StackPanel _drawingToolStackPanel; //商品信息 private GoodsInfo _goodsInfo; ///指标器容器 //private ComboBox _indicatorComboBox; public Grid _mainGrid; private BusyIndicator _exportBusyIndicator; /// /// 上一个鼠标样式 /// private PointerType _oldPointerType; private Grid _pointerGrid; private Popup _pointerPopup; /// /// 当前鼠标样式,初始为显示十字光标 /// private PointerType _pointerType; private Popup _popup; /// /// The timespan control /// private QuickTimeSpan _timeSpanControl; //显示标签控件 //private TextBlock _symbolTextBlock; private Image _toolImage; /// /// 当前图中添加图表容器 /// private List _chartPanes = new List(); private DispatcherTimer _timeSharingTimer; private DisplayFormula timeSharingFormulaModel; //分时图实体,用于更新当前分时图数据 private DisplayFormula _tikFormulaModel; //即时图,更新Tik图表数据 private DisplayFormula _minuteFormulaModel; // private Dictionary _comparisonFommulaHsSet = new Dictionary(); private List _chartCachePanes = new List(); //缓存切换周期时商品状态 private static readonly object _lockObject = new Object(); #endregion Fields #region Constructors /// /// 静态构造函数 /// static Chart() { DefaultStyleKeyProperty.OverrideMetadata(typeof(Chart), new FrameworkPropertyMetadata(typeof(Chart))); } /// /// 构造函数 /// public Chart() { this.Focusable = true; //默认加载简体中文 LanguageManager.SetLanguage(CultureInfo.InstalledUICulture.Name); _dateScaleDisplay = new DateScaleDisplay(this); DateScaler = new AllDateScaler(this); _toolImage = new Image(); DateChartSlider = new DateChartSlider(this); TimeSpanPicker = new QuickTimeSpan(this); RightBuffer = 0; //ChartRedrawTimer.Interval = new TimeSpan(0, 0, 0, 0, ChartRedrawSpeed); _oldDrawingTool = new DrawingTrendLine(); _pointerType = PointerType.CursorWithData; _oldPointerType = PointerType.CursorWithData; LockPainting = false; ComparisionDictionary = new Dictionary>(); _timeSharingTimer = new DispatcherTimer(); _timeSharingTimer.Interval = new TimeSpan(0, 0, 1, 0); _timeSharingTimer.Tick += _timeSharingTimer_Tick; ComparisionTimeShareDic = new Dictionary>(); } #endregion Constructors #region Properties #region Public Properties /// /// ChartRedrawSpeed,图表重绘速度,用于鼠标拖动,画图工具拖动等,单位为毫秒 /// public int ChartRedrawSpeed = 1000 / 30; /// /// ChartRedrawTimer,图表重绘定时器 /// DispatcherTimer ChartRedrawTimer = new DispatcherTimer(); private const string _defaoltIndecator = "EMA4,MACD"; private DateTime _goodsQuoteTime; //分时图行情时间 /// /// 左右键增减周期值 /// public int LeftRightKeyToAcceleration { get { return (int)GetValue(LeftRightKeyToAccelerationProperty); } set { SetValue(LeftRightKeyToAccelerationProperty, value); } } /// /// 加速因数 /// public int AcceleratedWheelFactor { get { return (int)base.GetValue(Chart.AcceleratedWheelFactorProperty); } set { base.SetValue(Chart.AcceleratedWheelFactorProperty, value); } } /// /// 加速鼠标中键缩放键 /// public ModifierKeys AcceleratedWheelModifierKeys { get { return (ModifierKeys)base.GetValue(Chart.AcceleratedWheelModifierKeysProperty); } set { base.SetValue(Chart.AcceleratedWheelModifierKeysProperty, value); } } /// /// 获取或设置一个用于描述控件背景的画笔。 /// public SolidColorBrush ChartBackground { get { return (SolidColorBrush)base.GetValue(Chart.ChartBackgroundProperty); } set { base.SetValue(Chart.ChartBackgroundProperty, value); } } /// /// Gets the color of the chart background. /// public Color ChartBackgroundColor { get { return ColorHelper.ColorFromBrush(ChartBackground); } } /// /// 获取或设置字号。 /// public double ChartFontSize { get { var value = (double)base.GetValue(Chart.ChartFontSizeProperty); if (value <= 0) value = 12; return value; } set { if (value <= 0) value = 12; base.SetValue(Chart.ChartFontSizeProperty, value); } } /// /// 图表语言类型 /// public ChartLanguageType ChartLanguage { get { return (ChartLanguageType)base.GetValue(Chart.ChartLanguageProperty); } set { base.SetValue(Chart.ChartLanguageProperty, value); } } /// /// Gets or sets the chart time span. /// /// The chart time span. public TimeSpan ChartTimeSpan { get { return (TimeSpan)GetValue(ChartTimeSpanProperty); } set { SetValue(ChartTimeSpanProperty, value); } } public IDrawingTool CurrentDrawingTool { get { return _currentDrawingTool; } set { _currentDrawingTool = value; bool flag = value != null; if (flag) { PointerType = PointerType.Drawing; } } } ///// ///// 商品信息 //// public GoodsInfo CurrentGoods { get { return (GoodsInfo)GetValue(CurrentGoodsProperty); } set { SetValue(CurrentGoodsProperty, value); } } /// /// 商品周期 /// public List CycleItems { get { return (List)GetValue(CycleItemsProperty); } set { SetValue(CycleItemsProperty, value); } } /// /// 根据周期生成TimeSpan /// public TimeSpan CurrentTimeFrame { get { switch (CycleType) { case CycleType.Minute: return new TimeSpan(0, 1, 0); case CycleType.Minute3: return new TimeSpan(0, 3, 0); case CycleType.Minute5: return new TimeSpan(0, 5, 0); case CycleType.Minute10: return new TimeSpan(0, 10, 0); case CycleType.Minute15: return new TimeSpan(0, 15, 0); case CycleType.Minute30: return new TimeSpan(0, 30, 0); case CycleType.Minute60: return new TimeSpan(1, 0, 0); case CycleType.Minute90: return new TimeSpan(1, 30, 0); case CycleType.Minute120: return new TimeSpan(2, 0, 0); case CycleType.Minute180: return new TimeSpan(3, 0, 0); case CycleType.Minute240: return new TimeSpan(4, 0, 0); case CycleType.Hour: return new TimeSpan(1, 0, 0); case CycleType.Day: return new TimeSpan(1, 0, 0, 0); case CycleType.Week: return new TimeSpan(7, 0, 0, 0, 0); case CycleType.Month: return new TimeSpan(30, 0, 0, 0); case CycleType.Quarter: return new TimeSpan(90, 0, 0, 0); case CycleType.Year: return new TimeSpan(255, 0, 0, 0); case CycleType.Custom: return ChartTimeSpan; default: //分时图为1分 return new TimeSpan(0, 1, 0); } } } /// /// 是否在周期内 /// public bool IsNotInCycle(DateTime tikTime, ref DateTime CycleDate) { var Rlt = default(bool); if (DataSet != null && DataSet.DataPoints != null && DataSet.DataPoints.Count() > 0 && tikTime != DateTime.MinValue) { var dt = DataSet.DataPoints.Last().Date; var ts = tikTime - dt; var newDt = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0).AddMinutes(CurrentTimeFrame.TotalMinutes); switch (CycleType) { case CycleType.Minute: case CycleType.Minute3: case CycleType.Minute5: case CycleType.Minute10: case CycleType.Minute15: case CycleType.Minute30: case CycleType.Minute60: case CycleType.Minute90: case CycleType.Minute120: case CycleType.Minute180: case CycleType.Minute240: case CycleType.Custom: case CycleType.Hour: Rlt = tikTime >= newDt; if (Rlt) { CycleDate = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0).AddMinutes((Math.Floor((ts.TotalMinutes / CurrentTimeFrame.TotalMinutes)) * CurrentTimeFrame.TotalMinutes)); } break; case CycleType.Day: Rlt = tikTime >= new DateTime(dt.Year, dt.Month, dt.Day ).AddDays(1); if (Rlt) { CycleDate = new DateTime(dt.Year, dt.Month, dt.Day).AddDays((Math.Floor((ts.TotalDays / CurrentTimeFrame.TotalDays)) * CurrentTimeFrame.TotalDays)); } break; case CycleType.Week: Rlt = tikTime >= new DateTime(dt.Year, dt.Month, dt.Day ).AddDays((7 - ((int)(DayOfWeek)dt.DayOfWeek))); if (Rlt) { CycleDate = new DateTime(dt.Year, dt.Month, dt.Day).AddDays((Math.Floor((ts.TotalDays / CurrentTimeFrame.TotalDays)) * CurrentTimeFrame.TotalDays)); } break; case CycleType.Month: Rlt = tikTime >= new DateTime(dt.Year, dt.Month , 1).AddMonths(1); if (Rlt) { CycleDate = new DateTime(dt.Year, tikTime.Month, 1); } break; case CycleType.Quarter: Rlt = tikTime >= new DateTime(dt.Year, dt.Month , 1).AddMonths(3); if (Rlt) { var mm = (Math.Floor((ts.TotalDays / CurrentTimeFrame.TotalDays)) * CurrentTimeFrame.TotalDays); CycleDate = new DateTime(dt.Year, dt.Month, 1).AddDays(mm); } break; case CycleType.Year: Rlt = tikTime >= new DateTime(dt.Year + 1, 1, 1); if (Rlt) { CycleDate = new DateTime(tikTime.Year, 1, 1); } break; default: Rlt = false; break; } //Console.WriteLine("time:{0} tikTime:{1} newdate Rlt:{2} RltTime:{3}", dt.ToString(), tikTime.ToString(), newDt.ToString(), Rlt.ToString(), CycleDate.ToString()); } return Rlt; } /// /// 商品周期 /// public CycleType CycleType { get { return (CycleType)base.GetValue(Chart.CycleTypeProperty); } set { if (value != CycleType.Custom) { ChartTimeSpan = default(TimeSpan); } base.SetValue(Chart.CycleTypeProperty, value); } } /// /// 数据滑块 /// public DateChartSlider DateChartSlider { get; set; } /// /// Gets the drawing min chart span percent. /// public float DrawingMinChartSpanPercent { get { return 5f; } } /// /// 画线工具位置 /// public DrawingToolPositionType DrawingToolPosition { get { return (DrawingToolPositionType)base.GetValue(Chart.DrawingToolPositionProperty); } set { base.SetValue(Chart.DrawingToolPositionProperty, value); } } /// /// 当前画线工具 /// public DrawingToolType DrawingToolType { get { return (DrawingToolType)base.GetValue(Chart.DrawingToolTypeProperty); } set { base.SetValue(Chart.DrawingToolTypeProperty, value); } } /// /// 蜡烛图叠加是否填充 /// public bool FillComparisonCandle { get { return (bool)GetValue(FillComparisonCandleProperty); } set { SetValue(FillComparisonCandleProperty, value); } } /// /// 获取或设置一个用于描述控件背景的画笔。 /// public double GridOpacity { get { return (double)base.GetValue(Chart.GridOpacityProperty); } set { base.SetValue(Chart.GridOpacityProperty, value); } } /// /// Gets a value indicating whether this instance is dark background. /// public bool IsDarkBackground { get { var brush = ChartBackground as SolidColorBrush; if (brush != null) { return ColorHelper.ColorIsDark(brush.Color); } return false; } } /// /// Gets a value indicating whether [limit drawings by span]. /// public bool LimitDrawingsBySpan { get { return true; } } /// /// 获取或设置一个用于描述控件背景的画笔。 /// public LinePlotType LinePlotType { get { return (LinePlotType)base.GetValue(Chart.LinePlotTypeProperty); } set { base.SetValue(Chart.LinePlotTypeProperty, value); } } /// /// 最大加载数据条数 /// public int MaxLoadRecordCount { get { return (int)GetValue(MaxLoadRecordCountProperty); } set { SetValue(MaxLoadRecordCountProperty, value); } } /// /// 最大条数 /// public int MaxRecordCount { get { return (int)base.GetValue(Chart.MaxRecordCountProperty); } set { base.SetValue(Chart.MaxRecordCountProperty, value); } } /// /// 最小条数 /// public int MinRecordCount { get { return (int)base.GetValue(Chart.MinRecordCountProperty); } set { base.SetValue(Chart.MinRecordCountProperty, value); } } /// /// 返回叠加商品 /// public List ComparisonGoodsList { get { return _comparisonGoodsList; } } /// /// 分时图使用的显示最大点数据,根据开收盘时间计算 /// private int _timeSharingMaxPointCount = 60; internal int MaxRecordCountInternal { get { if (CycleType == CycleType.TimeSharing) { return _timeSharingMaxPointCount; } else { return MaxRecordCount; } } } internal int MinRecordCountInternal { get { if (CycleType == CycleType.TimeSharing) { return _timeSharingMaxPointCount; } else { return MinRecordCount; } } } internal int RecordCountInternal { get { if (CycleType == CycleType.TimeSharing) { return _timeSharingMaxPointCount; } else { return RecordCount; } } } /// /// Gets or sets the type of the pointer. /// /// The type of the pointer. public PointerType PointerType { get { return _pointerType; } set { bool flag = value != _pointerType; if (flag) { bool flag2 = value != PointerType.Drawing && value != PointerType.Erase; if (flag2) { _oldPointerType = value; } _pointerType = value; flag2 = (_pointerType != PointerType.Drawing); if (flag2) { CurrentDrawingTool = null; } } } } /// /// 显示数据条数 /// public int RecordCount { get { return (int)base.GetValue(Chart.RecordCountProperty); } set { base.SetValue(Chart.RecordCountProperty, value); } } /// /// Gets or sets the show drawing tool. /// public Visibility ShowDrawingTool { get { return (Visibility)base.GetValue(Chart.ShowDrawingToolProperty); } set { base.SetValue(Chart.ShowDrawingToolProperty, value); } } /// /// Gets or sets a value indicating whether [show legend arrows]. /// public bool ShowLegendArrows { get; set; } /// /// Gets or sets the time span picker. /// public QuickTimeSpan TimeSpanPicker { get { return _timeSpanControl; } set { _timeSpanControl = value; } } #endregion Public Properties #region Internal Properties /// /// 叠加商品字典 /// internal Dictionary> ComparisionDictionary { get; set; } /// /// 分时图商品字典 /// internal Dictionary> ComparisionTimeShareDic { get; set; } /// /// Gets the current symbol. /// internal string CurrentSymbol { get { var goods = CurrentGoods; if (goods == null || string.IsNullOrWhiteSpace(goods.Symbol)) return string.Empty; return goods.Symbol; } } /// /// 数据集 /// internal ChartDataSet DataSet { get; set; } /// /// Gets or sets the date scaler. /// internal IDateScaler DateScaler { get; set; } private int _offSetNum = 0; /// /// Gets or sets a value indicating whether [lock painting]. /// internal bool LockPainting { get; set; } /// /// 是否为分时图,控制部分内容不显示 /// //internal bool IsTimeShare //{ // get; // set; //} #endregion Internal Properties #endregion Properties #region Methods #region Public Methods /// /// 添加指标窗口 /// public void AddIndicator() { //todo: //var formula = new FormulaModel(); //formula.FormulaName = indicatorType; //var displayFormula = new DisplayFormula(formula, DataSet, this); // AddPlot(displayFormula, null); var listPlots = DisplayFormula.CreateDisplayFormula(DataSet, this); if (listPlots != null) { ChartPane pane = null; foreach (var displayFormula in listPlots) { pane = AddFormula(displayFormula, false, pane); } } Refresh(true); } /// /// 清除叠加商品 /// public void ClearComparison() { if (_comparisonGoodsList != null) _comparisonGoodsList.Clear(); if ((ComparisionDictionary == null || !ComparisionDictionary.Any()) && (ComparisionTimeShareDic == null || !ComparisionTimeShareDic.Any())) return; ComparisionDictionary.Clear(); var mainScalelayer = GetMainScaleLayer(); if (mainScalelayer == null) return; var plots = mainScalelayer.AllPlots.Where(z => z is ComparisonPlot); if (plots == null || !plots.Any()) return; var comparsions = plots.ToList(); foreach (var comparison in comparsions) { comparison.Delete(); } mainScalelayer.ComparisonFormulas.Clear(); //清除分时图叠加 if (ComparisionTimeShareDic != null && ComparisionTimeShareDic.Any()) { ComparisionTimeShareDic.Clear(); } if (_comparisonFommulaHsSet != null && _comparisonFommulaHsSet.Any()) { _comparisonFommulaHsSet.Clear(); } if (timeSharingFormulaModel != null) { var compesionsPlots = timeSharingFormulaModel.ScaleLayer.AllPlots.Where(z => z is ComparisonPlot); if (compesionsPlots.Any()) return; comparsions = compesionsPlots.ToList(); foreach (var comparison in comparsions) { comparison.Delete(); } } Refresh(); } /// /// 在派生类中重写后,每当应用程序代码或内部进程调用 ,都将调用此方法。 /// public override void OnApplyTemplate() { base.OnApplyTemplate(); try { BuildParts(); //初始化画图工具栏 InitializeDrawingTool(); //添加历史价格图和成交量图 AddDefaultIndicators(); //添加图表底部控件 BuildBottomControls(); } catch { ;} } /// /// 设置叠加商品 /// /// The goods list. public void SetComparisonGoodsList(List goodsList) { Dispatcher.BeginInvoke(new Action(() => { if (goodsList == null || !goodsList.Any()) return; ClearComparison(); if (this.CycleType == CycleType.TimeSharing) { foreach (var goods in goodsList) { AddComparisonFormula(null, null, goods); } _comparisonGoodsList = goodsList; Refresh(true); return; } //Clear里有清空变量,放在后面 _comparisonGoodsList = goodsList; var chartPane = GetMainChartPane(); //要叠加的容器 foreach (var goods in goodsList) { //不叠加空和自己 if (goods == null || string.IsNullOrWhiteSpace(goods.Symbol) || string.CompareOrdinal(goods.Symbol, CurrentSymbol) == 0) continue; var dataPoints = LoadComparisonDataSet(goods); if (dataPoints == null) continue; ComparisionDictionary.Add(goods.Symbol, dataPoints); //添加叠加商品 if (chartPane != null) { AddComparisonFormula(new ChartDataSet(dataPoints), chartPane, goods); } } Refresh(true); }), DispatcherPriority.Background); } /// /// 更新数据集合-行情组件取服务数据回发时调用 /// /// The points. public void UpdateDataPoints(IEnumerable points) { Task.Factory.StartNew(new Action(() => { var lineDataPoints = points == null ? null : points.ToList(); if (lineDataPoints == null || !lineDataPoints.Any()) return; DataSet = DataSet ?? new ChartDataSet(); //集合都要按时间顺排序,才能画图 var oldDataPoints = DataSet.DataPoints ?? new List(); var totalDataPoints = lineDataPoints.Union(oldDataPoints, new DataPointComparer()); DataSet.DataPoints = CaclulateCycleDataIncrease(totalDataPoints.OrderBy(z => z.Date).ToList()); Dispatcher.BeginInvoke(new Action(() => { var allDateScaler = (AllDateScaler)DateScaler; allDateScaler.SetDates(DataSet.DataPoints, CurrentTimeFrame, false); Refresh(true); //刷新叠加商品 RefreshComparsionGoods(); }), DispatcherPriority.Background); })); } /// /// 更新数据集合-行情组件取服务数据回发时调用 /// /// The points. public void UpdateTimeSharingDataPoints(IEnumerable points) { Dispatcher.BeginInvoke(new Action(() => { var lineDataPoints = points == null ? null : points.ToList(); if (lineDataPoints == null || !lineDataPoints.Any()) return; DataSet = DataSet ?? new ChartDataSet(); //集合都要按时间顺排序,才能画图 DataSet.DataPoints = points.OrderBy(z => z.Date).ToList(); if (timeSharingFormulaModel == null) return; { timeSharingFormulaModel.UpdateTimeSharingData(DataSet); } Refresh(true); }), DispatcherPriority.Background); } /// /// 更新叠加商品数据集合 /// /// The comparison symbol. /// The points. public void UpdateComparisonDataPoints(string comparisonSymbol, IEnumerable points) { Dispatcher.BeginInvoke(new Action(() => { if (_comparisonGoodsList == null || !_comparisonGoodsList.Any()) return; var comparisonGoods = _comparisonGoodsList.FirstOrDefault(z => z.Symbol == comparisonSymbol); if (comparisonGoods == null) return; //集合都要按时间顺排序,才能画图 var dataPoints = points ?? new List(); var oldDataPoints = ComparisionDictionary[comparisonSymbol] ?? new List(); var totalDataPoints = dataPoints.Union(oldDataPoints, new DataPointComparer()) .OrderBy(z => z.Date) .ToList(); //目前只能一个商品叠加,更新的时候清除所有 var list = new List(); list.AddRange(_comparisonGoodsList); #region 商品叠加 ClearComparison(); _comparisonGoodsList = list; ComparisionDictionary.Add(comparisonSymbol, totalDataPoints); var disPlayFormula = new DisplayFormula(new FormulaModel("main"), new ChartDataSet(totalDataPoints), this, comparisonGoods); var scaleLayer = GetMainScaleLayer(); if (scaleLayer == null) return; disPlayFormula.PlotBases.Clear(); scaleLayer.ComparisonFormulas.Clear(); foreach (var indicator in disPlayFormula.FormulaModel.IndicatorList) { var comparisonPlot = new ComparisonPlot(indicator, this, comparisonGoods) { IsActive = true, ScaleLayer = scaleLayer }; scaleLayer.AllPlots.Add(comparisonPlot); disPlayFormula.PlotBases.Add(comparisonPlot); //添加到公式中 } scaleLayer.ComparisonFormulas.Add(disPlayFormula); disPlayFormula.ScaleLayer = scaleLayer; #endregion Refresh(true); }), DispatcherPriority.Background); } /// /// 导出数据到Excel /// public void ExportToExcel() { //正在下载数据则返回 if (_exportBusyIndicator.IsBusy) return; if (this.CurrentGoods == null) return; if (this.DataSet == null || this.DataSet.DataPoints == null || !this.DataSet.DataPoints.Any()) return; var width = this.ActualWidth; this.Width = width; var displayName = string.IsNullOrWhiteSpace(this.CurrentGoods.DisplayName) ? this.CurrentGoods.GoodsCode : this.CurrentGoods.DisplayName; var exportHelper = new ExportHelper(); exportHelper.OnStartExport += OnStartExport; exportHelper.OnExportCompleted += OnExportCompleted; exportHelper.ExportToExcel(displayName, CycleType, this.DataSet.DataPoints, this.CurrentGoods.MinUnit); } /// /// 数据导出开始 /// /// The sender. /// The instance containing the event data. private void OnStartExport(object sender, EventArgs e) { //要在SaveFileDiaolog关闭后再启动BusyIndicator,否则界面会有SaveFileDiaolog窗口的黑影 _exportBusyIndicator.IsBusy = true; } /// /// 数据导出完成 /// /// The message. /// The caption. private void OnExportCompleted(string message, string caption) { _exportBusyIndicator.IsBusy = false; //message为空,只不弹出信息 if (string.IsNullOrWhiteSpace(message)) return; System.Windows.MessageBox.Show(Application.Current.MainWindow, message, caption); } #endregion Public Methods #region Internal Methods /// /// sts. /// /// The parent. /// The indicator model. /// if set to true [allow defaults]. /// PlotBase. internal PlotBase AddChildPlot(LinePlot parent, IndicatorModel indicatorModel, bool AllowDefaults) { ScaleLayer scaleLayer = parent.ScaleLayer; bool flag = scaleLayer != null; PlotBase result = null; if (flag) { //bool flag2 = aCalc is ILineCalculation; //if (flag2) //{ var linePlot = new LinePlot(indicatorModel, parent.GetChart()); //flag2 = (aCalc is calcVolumeAtPrice); //if (flag2) //{ // linePlot.PlotOpacity = ((calcVolumeAtPrice)aCalc).DefaultOpacity(); //} linePlot.InitAsNew(); //if (AllowDefaults) //{ // WebChart_5948(linePlot); //} // scaleLayer.AddPlot(linePlot); linePlot.ScaleLayer = scaleLayer; result = linePlot; //} //else //{ //flag2 = (aCalc is IChannelCalc); //if (flag2) //{ // ChannelPlot channelPlot = new ChannelPlot(parent.LinePlot_4866(), (IChannelCalc)aCalc); // channelPlot.InitAsNew(); // if (AllowDefaults) // { // WebChart_5948(channelPlot); // } // scaleLayer.ScaleLayer_5530(channelPlot); // channelPlot.ScaleLayer = scaleLayer; // WebChart_2077 = true; // result = channelPlot; //} //else //{ // flag2 = (aCalc is ITwoLineCalc); // if (flag2) // { // TwoLinePlot twoLinePlot = new TwoLinePlot(parent.LinePlot_4866(), (ITwoLineCalc)aCalc); // twoLinePlot.InitAsNew(); // if (AllowDefaults) // { // WebChart_5948(twoLinePlot); // } // scaleLayer.ScaleLayer_5530(twoLinePlot); // twoLinePlot.ScaleLayer = scaleLayer; // WebChart_2077 = true; // result = twoLinePlot; // } //} //} } return result; } /// /// 添加叠加商品 /// public void AddComparisonFormula(ILineDataSet dataSet, ChartPane addToPane, GoodsInfo goodsInfo) { if (this.CycleType == CycleType.TimeSharing && this.timeSharingFormulaModel != null) { if (goodsInfo == null || goodsInfo.GoodsCode == this.CurrentGoods.GoodsCode || ComparisionTimeShareDic.ContainsKey(goodsInfo.GoodsCode)) return; var dataList = OnLoadHistoryTimeSpaningCycleData(CurrentGoods, _timeSharingMaxPointCount, timeSharingFormulaModel.OpenTime, timeSharingFormulaModel.CloseTime); var diplayformula = DisplayFormula.TimeSharingLineFormula(this, goodsInfo, dataList); ComparisionTimeShareDic[goodsInfo.GoodsCode] = dataList; CreateComparisonPlot(diplayformula, this.timeSharingFormulaModel.ScaleLayer); } else { var disPlayFormula = new DisplayFormula(new FormulaModel("main"), dataSet, this, goodsInfo); ChartPane pane = addToPane; bool flag = addToPane == null; if (flag) { pane = new ChartPane(this); } flag = (addToPane == null); if (flag) { //取最大grid高度做* var stars = _chartPanesGrid.RowDefinitions.Count > 0 ? _chartPanesGrid.RowDefinitions.Max(z => z.Height.Value) : 1.0; AddChartPane(pane, stars); } var scaleLayer = GetMainScaleLayer(); if (scaleLayer == null) return; disPlayFormula.PlotBases.Clear(); scaleLayer.ComparisonFormulas.Clear(); foreach (var indicator in disPlayFormula.FormulaModel.IndicatorList) { var comparisonPlot = new ComparisonPlot(indicator, this, goodsInfo); comparisonPlot.IsActive = true; comparisonPlot.ScaleLayer = scaleLayer; scaleLayer.AllPlots.Add(comparisonPlot); disPlayFormula.PlotBases.Add(comparisonPlot); //添加到公式中 } scaleLayer.ComparisonFormulas.Add(disPlayFormula); disPlayFormula.ScaleLayer = scaleLayer; } } /// /// 移除叠加商品 /// public void RemoveComparisonForLuma() { } /// /// 添加图例 /// /// /// internal void AddPlot(DisplayFormula formula, PlotBase childTarget) { bool flag = formula == null; if (!flag) { flag = (childTarget != null && childTarget.ScaleLayer != null && childTarget.ScaleLayer.GetChartPane() != null); if (flag) { var addToPane = childTarget.ScaleLayer.GetChartPane(); AddFormula(formula, true, addToPane); } else { AddFormula(formula, true); } } } internal List AllPanes() { var list = new List(); if (_chartPanesGrid == null || _chartPanesGrid.Children == null || _chartPanesGrid.Children.Count == 0) return list; list.AddRange(_chartPanesGrid.Children.OfType()); return list; } /// /// 下载前N条数据(N = MaxLoadRecordCount) /// internal void DownloadNextDataBlock() { var goods = CurrentGoods; var exchangeCode = CurrentGoods.ExchangeCode; var goodsCode = CurrentGoods.GoodsCode; var cycleType = CycleType; var timeSpan = CurrentTimeFrame; var maxLoadRecordCount = MaxLoadRecordCount; Task.Factory.StartNew(new Action(() => { //数据集有值,取最小时间, 无值,取当前时间 var maxTime = (DataSet != null && DataSet.DataPoints != null && DataSet.DataPoints.Any()) ? DataSet.DataPoints.Min(z => z.Date) : DateTime.Now; //减去周期ticks,作为当前下载最大值 maxTime = maxTime.AddTicks(-timeSpan.Ticks); if (DataSet == null) { DataSet = new ChartDataSet(); } List newData = null; var cycleTypeHandler = LoadHistoryCycleDataByCycleType; if (cycleTypeHandler != null) { newData = cycleTypeHandler(exchangeCode, goodsCode, cycleType, timeSpan, DateTime.MinValue, maxTime, maxLoadRecordCount); } var dataPoints = new List(); if (newData != null && newData.Any()) { dataPoints.AddRange(CaclulateCycleDataIncrease(newData)); if (DataSet.DataPoints != null && dataPoints.Any()) { //只更新前面数据块第一条数据涨幅 var firstOld = DataSet.DataPoints[0] as IOHLCDataPoint; var lastNew = newData[newData.Count - 1] as IOHLCDataPoint; if (firstOld != null && lastNew != null) { firstOld.Increase = lastNew.Close.Equals(0) ? 0f : (firstOld.Close - lastNew.Close) / lastNew.Close; firstOld.IncreaseValue = lastNew.Close.Equals(0) ? 0f : (firstOld.Close - lastNew.Close); } dataPoints.AddRange(DataSet.DataPoints); } DataSet.DataPoints = dataPoints.OrderBy(z => z.Date).ToList(); Dispatcher.BeginInvoke(new Action(() => { var allDateScaler = (AllDateScaler)DateScaler; allDateScaler.SetDates(DataSet.DataPoints, timeSpan, false); //前一数据块的最后时间做为当前的结束时间 allDateScaler.DesiredEndDate = maxTime.AddTicks(timeSpan.Ticks); Refresh(true); //刷新叠加商品 RefreshComparsionGoods(); }), DispatcherPriority.Background); } })); } /// /// 若画图则隐藏十字光标 /// internal void DrawingToolExecute() { if (PointerType != PointerType.Drawing) { if (_crossHairGrid.Visibility == Visibility.Collapsed) { _crossHairGrid.Visibility = Visibility.Visible; } //TODO:更改所有控件的背景颜色 var panes = AllPanes(); foreach (var chartPane in panes) { chartPane.OnDrawing(); } } else { if (_crossHairGrid.Visibility == Visibility.Visible) { _crossHairGrid.Visibility = Visibility.Collapsed; } } } internal double GetChartPanelOffset() { return _chartPanesGrid.ColumnDefinitions[0].ActualWidth; } /// /// 得到的图表窗格的宽度. /// /// System.Double. internal double GetChartPaneWidth() { //0:左纵坐标, 1:Chart, 2:右纵坐标 return Math.Round(_chartPanesGrid.ColumnDefinitions[1].ActualWidth) - RightBuffer; } /// /// 获取当前已打开的主图LinePlot /// /// PlotBase. internal T GetPlot(string formulaName) where T : PlotBase { var panes = AllPanes(); if (panes == null || !panes.Any()) return default(T); var name = formulaName.ToLower(); foreach (var chartPane in panes) { var scaleLayers = chartPane.AllScaleLayers; if (scaleLayers == null || !scaleLayers.Any()) continue; foreach (var scaleLayer in scaleLayers) { var plots = scaleLayer.AllPlots; if (plots == null || !plots.Any()) continue; foreach (var plot in plots) { if (plot != null && (plot is T) && plot.Indicator.ParentFormula.FormulaName.ToLower() == name) return plot as T; } } } return null; } private ScaleLayer GetMainScaleLayer() { var panes = AllPanes(); if (panes == null || !panes.Any()) return null; foreach (var chartPane in panes) { var scaleLayers = chartPane.AllScaleLayers; if (scaleLayers == null || !scaleLayers.Any()) continue; foreach (var scaleLayer in scaleLayers) { var formulas = scaleLayer.AllDisplayFormula; if (formulas == null || !formulas.Any()) continue; foreach (var formula in formulas) { if (formula != null && formula.FormulaModel.IsMain) { return scaleLayer; } } } } return null; } /// /// 初始化画图工具栏 /// internal void InitializeDrawingTool() { if (_drawingToolStackPanel == null) return; if (_drawingToolbar == null) _drawingToolbar = new ChartDrawingToolbar(this); if (_chartToolbarExpander == null) _chartToolbarExpander = new ChartToolbarExpander(_drawingToolbar); //隐藏画线工具展开按钮 _chartToolbarExpander.Visibility = Visibility.Hidden; switch (DrawingToolPosition) { case DrawingToolPositionType.Left: _drawingToolStackPanel.SetValue(Grid.RowProperty, 0); _drawingToolStackPanel.SetValue(Grid.RowSpanProperty, 4); _drawingToolStackPanel.SetValue(Grid.ColumnProperty, 0); _drawingToolStackPanel.Orientation = Orientation.Vertical; _drawingToolStackPanel.HorizontalAlignment = HorizontalAlignment.Left; _drawingToolbar.UpdateLayout(Orientation.Vertical); break; case DrawingToolPositionType.Right: _drawingToolStackPanel.SetValue(Grid.RowProperty, 0); _drawingToolStackPanel.SetValue(Grid.RowSpanProperty, 4); _drawingToolStackPanel.SetValue(Grid.ColumnProperty, 2); _drawingToolStackPanel.Orientation = Orientation.Vertical; _drawingToolStackPanel.HorizontalAlignment = HorizontalAlignment.Right; _drawingToolbar.UpdateLayout(Orientation.Vertical); break; case DrawingToolPositionType.Top: _drawingToolStackPanel.SetValue(Grid.RowProperty, 1); _drawingToolStackPanel.SetValue(Grid.RowSpanProperty, 1); _drawingToolStackPanel.SetValue(Grid.ColumnProperty, 1); _drawingToolStackPanel.Orientation = Orientation.Horizontal; _drawingToolStackPanel.HorizontalAlignment = HorizontalAlignment.Stretch; _chartToolbarExpander.VerticalAlignment = VerticalAlignment.Top; _drawingToolbar.UpdateLayout(Orientation.Horizontal); break; case DrawingToolPositionType.Bottom: _drawingToolStackPanel.SetValue(Grid.RowProperty, 3); _drawingToolStackPanel.SetValue(Grid.RowSpanProperty, 1); _drawingToolStackPanel.SetValue(Grid.ColumnProperty, 1); _drawingToolStackPanel.Orientation = Orientation.Horizontal; _drawingToolStackPanel.HorizontalAlignment = HorizontalAlignment.Stretch; _chartToolbarExpander.VerticalAlignment = VerticalAlignment.Top; _drawingToolbar.UpdateLayout(Orientation.Horizontal); break; } if (!_drawingToolStackPanel.Children.Contains(_chartToolbarExpander)) _drawingToolStackPanel.Children.Add(_chartToolbarExpander); if (!_drawingToolStackPanel.Children.Contains(_drawingToolbar)) _drawingToolStackPanel.Children.Add(_drawingToolbar); } private int i = 0; private double currentX = 0; private double currentY = 0; /// /// 刷新图表 /// /// if set to true [is updata set]. internal void Refresh(bool isUpdataSet = false) { lock (_lockObject) { if (!LockPainting) { Dispatcher.BeginInvoke(new Action(DrawingChart), DispatcherPriority.Background, isUpdataSet); } } } internal void RemoveChartPane(ChartPane aPane) { int num = Conversions.ToInteger(aPane.GetValue(Grid.RowProperty)); _chartPanesGrid.Children.Remove(aPane); _chartPanes.Remove(aPane); if (aPane.RightScaleControl != null) { _chartPanesGrid.Children.Remove(aPane.RightScaleControl); } if (aPane.LeftScaleControl != null) { _chartPanesGrid.Children.Remove(aPane.LeftScaleControl); } _chartPanesGrid.RowDefinitions.RemoveAt(num); UIElement currentSplitter = null; foreach (var uiElement in _chartPanesGrid.Children) { var current = uiElement as UIElement; if (current != null && (current is ChartPane || current is ValueScaleDisplayCollection || current is GridSplitter)) { int rowIndex = Conversions.ToInteger(current.GetValue(Grid.RowProperty)); if (rowIndex < 1) continue; if (rowIndex > num) { current.SetValue(Grid.RowProperty, rowIndex - 2 < 0 ? 0 : rowIndex - 2); } else if (rowIndex == num - 1 && current is GridSplitter) { currentSplitter = current; } } } if (currentSplitter != null) { _chartPanesGrid.Children.Remove(currentSplitter); _chartPanesGrid.RowDefinitions.RemoveAt(num - 1); } aPane.RemoveAllScaleLayers(); Refresh(true); } internal void SetCursorImagePos(bool showIt, MouseEventArgs e) { if (showIt) { switch (PointerType) { case PointerType.CursorWithData: _crossHairGrid.Cursor = Cursors.Arrow; _chartPanesGrid.Cursor = Cursors.Arrow; _toolImage.Source = null; break; case PointerType.Cursor: _crossHairGrid.Cursor = Cursors.Arrow; _chartPanesGrid.Cursor = Cursors.Arrow; _toolImage.Source = null; break; case PointerType.Pan: _crossHairGrid.Cursor = Cursors.Hand; _chartPanesGrid.Cursor = Cursors.Hand; _toolImage.Source = null; break; case PointerType.Erase: _crossHairGrid.Cursor = Cursors.Arrow; _toolImage.Source = ImageHelper.GetImage("Eraser.png"); break; case PointerType.Drawing: _toolImage.Source = CurrentDrawingTool == null ? null : CurrentDrawingTool.Icon(); break; } if (_toolImage.Source == null) { CleanQuoteBoard(); } else { if (_popup == null) { _popup = new Popup { Child = _toolImage, Placement = PlacementMode.Relative, AllowsTransparency = true }; _chartPanesGrid.Children.Add(_popup); _popup.IsOpen = true; } } } else { CleanQuoteBoard(); } //} if (showIt && e != null && _toolImage.Source != null) { Point position = e.GetPosition(_chartPanesGrid); _popup.HorizontalOffset = (position.X + 8.0); _popup.VerticalOffset = (position.Y - 8.0); } else { if (!showIt) { CleanQuoteBoard(); } } } /// /// 设置当前画图工具为null /// internal void SetDrawingTool() { _oldDrawingTool = CurrentDrawingTool; CurrentDrawingTool = null; _pointerType = _oldPointerType; DrawingToolExecute(); var panes = AllPanes(); foreach (var current in panes) { current.OnDrawing(); } _drawingToolbar.ButtonPress(); } /// /// 显示/隐藏Download More Button /// /// if set to true [is show]. internal void ShowDownloadButton(bool isShow) { if (isShow) { _downloadMoreButton.Width = DateChartSlider.Height; _downloadMoreButton.Height = DateChartSlider.Height; _downloadMoreButton.Visibility = Visibility.Visible; } else { _downloadMoreButton.Visibility = Visibility.Collapsed; } } #endregion Internal Methods #region Protected Methods /// /// 鼠标滚轮事件 /// /// 包含事件数据的 。 protected override void OnMouseWheel(MouseWheelEventArgs e) { e.Handled = true; var factor = 1; if ((Keyboard.Modifiers & AcceleratedWheelModifierKeys) == AcceleratedWheelModifierKeys) { factor = AcceleratedWheelFactor; } int num = DateScaler.NumBars + (factor) * (int)Math.Round((double)e.Delta / 120.0); num = num < 5 ? 5 : num; var scalerCount = DateScaler.Count(); num = num > scalerCount ? scalerCount : num; DateScaler.NumBars = num; Refresh(); } #endregion Protected Methods #region Private Static Methods private static void ChartFontSizeProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = d as Chart; if (chart == null) return; UpdateDrawingTool(chart); } private static void ChartLanguageProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { ChartLanguageType language; if (!Enum.TryParse(e.NewValue.ToString(), out language)) { language = ChartLanguageType.SimplifiedChinese; } switch (language) { case ChartLanguageType.SimplifiedChinese: LanguageManager.SetLanguage("zh-CN"); break; case ChartLanguageType.TraditionalChinese: LanguageManager.SetLanguage("zh-TW"); break; case ChartLanguageType.English: LanguageManager.SetLanguage("en-US"); break; default: LanguageManager.SetLanguage("zh-CN"); break; } var chart = d as Chart; if (chart == null) return; UpdateDrawingTool(chart); } private static void ChartLineTypePlotProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = (Chart)d; //更新plot type var newValue = (LinePlotType)e.NewValue; var pricePlot = chart.GetPlot("main"); if (pricePlot != null && pricePlot.Indicator != null) pricePlot.Indicator.LinePlotType = newValue; var comparisonPlot = chart.GetPlot("main"); if (comparisonPlot != null && comparisonPlot.Indicator != null) comparisonPlot.Indicator.LinePlotType = newValue; chart.Refresh(); } private static void ChartCycleBarVisibity(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = (Chart)d; if (chart.TimeSpanPicker != null) { chart.TimeSpanPicker.Visibility = (Visibility)e.NewValue; } } /// /// 图的最小最大记录改变. /// /// The d. /// The instance containing the event data. private static void ChartMinMaxRecordProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = (Chart)d; var numBars = chart.DateScaler.NumBars; var oldNum = (int)e.OldValue; var newNum = (int)e.NewValue; //numBars为矩形柱间距,矩形柱数量实际要-1 if (numBars == 4 || numBars == oldNum - 1) { chart.DateScaler.NumBars = newNum - 1; chart.Refresh(); } } /// /// Charts the property_ changed. /// /// The d. /// The instance containing the event data. private static void ChartProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = (Chart)d; if (chart == null) return; if ((e.Property == FillComparisonCandleProperty && chart.LinePlotType == LinePlotType.Candlestick && chart.ComparisionDictionary.Any()) || e.Property != FillComparisonCandleProperty) { chart.Refresh(); } } /// /// Charts the record count property_ changed. /// /// The d. /// The instance containing the event data. private static void ChartRecordCountProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = (Chart)d; //numBars为矩形柱间距,矩形柱数量实际要-1 chart.DateScaler.NumBars = (int)e.NewValue - 1; chart.Refresh(); } /// /// Charts the time span property_ changed. /// /// The d. /// The instance containing the event data. private static void ChartTimeSpanProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = (Chart)d; if (chart == null) return; if (chart.ChartTimeSpan != default(TimeSpan)) { if (chart.CycleType == CycleType.Custom) { //直接调用 chart.CycleTypePropertyChanged(false); } else { //通过设置CycleType触发数据下载 chart.CycleType = CycleType.Custom; } } } /// /// Currents the goods property_ changed. /// /// The d. /// The instance containing the event data. private static void CurrentGoodsProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = (Chart)d; if (chart == null) return; // chart.ClearComparison(); chart.OnCycleTypeChanged(true); } /// /// 周期属性改变后回调函数 /// /// The Dependency Object. /// The instance containing the event data. private static void ChartCycleTypeProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = (Chart)d; if (chart == null) return; chart.CycleTypePropertyChanged(CycleType.TimeSharing.Equals(e.OldValue) || CycleType.Tik.Equals(e.OldValue)); } /// /// 周期集合属性改变后回调函数 /// /// The Dependency Object. /// The instance containing the event data. private static void ChartCycleTypeItemsProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = (Chart)d; if (chart == null || chart.TimeSpanPicker == null) return; } /// /// Tik属性改变后回调函数 /// private static void ChartTikProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = (Chart)d; if (chart == null) return; if (chart.CurrentTik != null && chart.DataSet != null && chart.DataSet.DataPoints != null && chart.DataSet.DataPoints.Count() > 0) { var lastTik = chart.DataSet.DataPoints.Last() as BarDataPoint; var date = default(DateTime); if (chart.IsNotInCycle(chart.CurrentTik.Date, ref date)) { var item = new BarDataPoint(date, chart.CurrentTik.Open, chart.CurrentTik.High, chart.CurrentTik.Low, chart.CurrentTik.Close, chart.CurrentTik.Volume, chart.CurrentTik.Turnover, chart.CurrentTik.HoldVolume); chart.DataSet.DataPoints.Add(item); chart.SetDatesAndRefresh(true,false); } else { var barPointTmp = chart.DataSet.DataPoints.Last() as BarDataPoint; barPointTmp.Close = chart.CurrentTik.Close; barPointTmp.High = chart.CurrentTik.Value > lastTik.High ? chart.CurrentTik.Value : lastTik.High; barPointTmp.Low = chart.CurrentTik.Value < lastTik.Low ? chart.CurrentTik.Value : lastTik.Low; barPointTmp.HoldVolume = chart.CurrentTik.HoldVolume; barPointTmp.Turnover = chart.CurrentTik.Turnover; barPointTmp.Volume += chart.CurrentTik.Volume; chart.SetDatesAndRefresh(true, false); } } } /// /// Drawings the tool position property_ changed. /// /// The d. /// The instance containing the event data. private static void DrawingToolPositionProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = (Chart)d; chart.InitializeDrawingTool(); } /// /// Currents the drawing tool type_ changed. /// /// The d. /// The instance containing the event data. private static void DrawingToolType_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var chart = (Chart)d; if (chart == null || chart._drawingToolbar == null) return; var button = chart._drawingToolbar.GetToolbarButton(chart.DrawingToolType); if (button == null) return; button.ButtonClick(); } /// /// Updates the drawing tool. /// /// The chart. private static void UpdateDrawingTool(Chart chart) { if (chart == null) return; //更新画图工具 if (chart._drawingToolStackPanel != null) { chart._drawingToolStackPanel.Children.Clear(); chart._drawingToolbar = new ChartDrawingToolbar(chart); chart._chartToolbarExpander = new ChartToolbarExpander(chart._drawingToolbar); chart.InitializeDrawingTool(); } //更新图表 chart.Refresh(); } #endregion Private Static Methods #region Private Methods /// /// 刷新叠加商品 /// private void RefreshComparsionGoods() { if (_comparisonGoodsList != null && _comparisonGoodsList.Any()) { var list = new List(); list.AddRange(_comparisonGoodsList); SetComparisonGoodsList(list); } } /// /// 获取主图所在的图表容器 /// /// private ChartPane GetMainChartPane() { foreach (var chartPane in _chartPanes) { if (IsMainChartPane(chartPane)) return chartPane; } return null; } /// /// 判断是否是主图容器 /// /// /// private bool IsMainChartPane(ChartPane chartPane) { foreach (var scaleLayer in chartPane.AllScaleLayers) { foreach (var displayFormula in scaleLayer.AllDisplayFormula) { if (displayFormula.FormulaModel != null && displayFormula.FormulaModel.IsMain) { return true; } } } return false; } /// /// 添加pane(图例) /// /// /// /// private ChartPane AddChartPane(ChartPane p, double stars, bool isMain = false) { int count = _chartPanesGrid.RowDefinitions.Count; if (count > 0) { AddRowDefinition(count, 1, GridUnitType.Auto); var splitter = new GridSplitter(); splitter.IsTabStop = (false); splitter.Background = (new SolidColorBrush(ColorHelper.DimGray)); splitter.SetValue(Grid.RowProperty, count); splitter.SetValue(Grid.ColumnProperty, 1); splitter.SetValue(Grid.ColumnSpanProperty, 2); splitter.HorizontalAlignment = HorizontalAlignment.Stretch; splitter.VerticalAlignment = VerticalAlignment.Center; splitter.Height = (2.0); splitter.Cursor = Cursors.SizeNS; splitter.Margin = new Thickness(0, 2, 0, 2); _chartPanesGrid.Children.Add(splitter); //AddRowDefinition(count + 1, stars <= 0.0 ? 1.0 : stars, GridUnitType.Star); AddRowDefinition(count + 1, isMain ? 2 : 1, GridUnitType.Star); } else { AddRowDefinition(count, isMain ? 2 : 1, GridUnitType.Star); //AddRowDefinition(count, stars <= 0.0 ? 1.0 : stars, GridUnitType.Star); } p.SetValue(Grid.RowProperty, count > 0 ? count + 1 : 0); p.SetValue(Grid.ColumnProperty, 1); _chartPanesGrid.Children.Add(p); _chartPanes.Add(p); if (p.RightScaleControl != null) { p.RightScaleControl.SetValue(Grid.RowProperty, count > 0 ? count + 1 : 0); p.RightScaleControl.SetValue(Grid.ColumnProperty, 2); _chartPanesGrid.Children.Add(p.RightScaleControl); } if (p.LeftScaleControl != null) { p.LeftScaleControl.SetValue(Grid.RowProperty, count > 0 ? count + 1 : 0); p.LeftScaleControl.SetValue(Grid.ColumnProperty, 0); _chartPanesGrid.Children.Add(p.LeftScaleControl); } return p; } /// /// 添加默认指标器-历史价格,成交量 /// private void AddDefaultIndicators() { AddFormula(new DisplayFormula(new FormulaModel("Main"), DataSet, this), true); var indecators = _defaoltIndecator.Split(','); foreach (var indecator in indecators) { AddIndicatorByName(indecator, false); } var menuItem = FormulaHelper.CreateIndicatorMenu("", IndicatorItemClick); menuItem.Header = "指标"; this.ContextMenu = new ContextMenu(); this.ContextMenu.Items.Add(menuItem); var menuItem1 = new MenuItem() { Header = "编辑参数" }; this.ContextMenu.Items.Add(menuItem1); menuItem1.Click += menuItem1_Click; var MainItem = new MenuItem() { Header = "清除主图" }; this.ContextMenu.Items.Add(MainItem); MainItem.Click += MainItem_Click; //打开公式管理器 MainItem = new MenuItem() { Header = "公式管理" }; this.ContextMenu.Items.Add(MainItem); MainItem.Click += (cs, ce) => { OpenFormulEditer(); }; } //清楚主图中的指标 void MainItem_Click(object sender, RoutedEventArgs e) { ClearMainPaneIndicator(); } /// /// 设置当前公式的参数 /// /// /// void menuItem1_Click(object sender, RoutedEventArgs e) { SetCurrectFormulaParam(); } //选择指标菜单时触发的事件处理 private void IndicatorItemClick(object sender, RoutedEventArgs e) { var selectItem = sender as MenuItem; if (selectItem != null && selectItem.Tag is ProgramModel) { var formulaModel = (selectItem.Tag as ProgramModel); AddIndicatorByName(formulaModel.Name); } } private void AddFormula(DisplayFormula displayFormula, bool allowDefaults) { AddFormula(displayFormula, allowDefaults, null, true); } public ChartPane AddFormula(DisplayFormula displayFormula, bool allowDefaults, ChartPane addToPane, bool isMain = false) { ChartPane pane = addToPane; bool flag = addToPane == null; if (flag) { pane = new ChartPane(this); //取最大grid高度做* var stars = _chartPanesGrid.RowDefinitions.Count > 0 ? _chartPanesGrid.RowDefinitions.Max(z => z.Height.Value) : 1.0; AddChartPane(pane, stars, isMain); } ScaleLayer scaleLayer = null; if (flag) { scaleLayer = new ScaleLayer(); pane.AddScaleLayer(scaleLayer); } else { if (IsMainChartPane(pane)) { scaleLayer = GetMainScaleLayer(); } else { scaleLayer = new ScaleLayer(); pane.AddScaleLayer(scaleLayer); } } displayFormula.PlotBases.Clear(); scaleLayer.AllDisplayFormula.Add(displayFormula); displayFormula.ScaleLayer = scaleLayer; pane.DisplayFristGrid(); return pane; } /// /// 添加指标时如主图指标则添加到主图,副图指标添加或替换.副图不能添加超过允许最大数量 /// /// 指标公式 /// /// /// public ChartPane AddFormulaFromPane(DisplayFormula displayFormula) { ChartPane pane = null; var flag = displayFormula.FormulaModel.IsMainIndicator; //var scaleLayer = new ScaleLayer(); ScaleLayer scaleLayer = new ScaleLayer(); if (flag) { pane = GetMainChartPane(); scaleLayer = GetMainScaleLayer(); } else { var maxChart = MaxChartPaneCount > 0 && _chartPanes.Count >= MaxChartPaneCount; if (!maxChart) //超过最大数量限制 { pane = new ChartPane(this); //取最大grid高度做* var stars = _chartPanesGrid.RowDefinitions.Count > 0 ? _chartPanesGrid.RowDefinitions.Max(z => z.Height.Value) : 1.0; AddChartPane(pane, stars); } else { pane = _chartPanes[_chartPanes.Count - 1]; pane.RemoveAllScaleLayers(); } scaleLayer = new ScaleLayer(); pane.AddScaleLayer(scaleLayer); } displayFormula.PlotBases.Clear(); scaleLayer.AllDisplayFormula.Add(displayFormula); displayFormula.ScaleLayer = scaleLayer; pane.DisplayFristGrid(); return pane; } /// /// 在ChartCont指点位置插入一行 /// /// /// /// private void AddRowDefinition(int index, double value, GridUnitType type) { var rowDefinition = new RowDefinition { Height = new GridLength(value, type) }; _chartPanesGrid.RowDefinitions.Insert(index, rowDefinition); } /// /// 添加图表底部控件 /// private void BuildBottomControls() { var grid = new Grid(); grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) }); grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) }); //隐藏用hidden,不用Collapsed,需要用来占位 _downloadMoreButtonPlaceHolder = new TextBlock() { Visibility = Visibility.Hidden }; _downloadMoreButton = new Button() { Visibility = Visibility.Collapsed }; _downloadMoreButton.Click -= DownloadMoreButton_Click; _downloadMoreButton.Click += DownloadMoreButton_Click; _downloadMoreButton.MouseEnter -= DownloadMoreButton_MouseEnter; _downloadMoreButton.MouseEnter += DownloadMoreButton_MouseEnter; _downloadMoreButton.SetValue(Grid.ColumnProperty, 0); _downloadMoreButtonPlaceHolder.SetValue(Grid.ColumnProperty, 0); var style = (Style)FindResource("LeftArrowButton"); _downloadMoreButton.Style = style; var gridRight = new Grid(); gridRight.Children.Add(DateChartSlider); gridRight.Children.Add(TimeSpanPicker); //不再大小图表上显示周期 gridRight.SetValue(Grid.ColumnProperty, 1); grid.Children.Add(gridRight); var gridLeft = new Grid { HorizontalAlignment = HorizontalAlignment.Right }; gridLeft.Children.Add(_downloadMoreButton); gridLeft.Children.Add(_downloadMoreButtonPlaceHolder); gridLeft.SetValue(Grid.ColumnProperty, 0); grid.Children.Add(gridLeft); _dateChartSliderStackPanel.Children.Add(_dateScaleDisplay); _dateChartSliderStackPanel.Children.Add(grid); } /// /// 生成控件 /// private void BuildParts() { _exportBusyIndicator = (BusyIndicator)TreeHelper.TemplateFindName("PART_BusyIndicator", this); _exportBusyIndicator.IsBusy = false; _exportBusyIndicator.BusyContent = LanguageManager.FindResource(LanguageConst.ExportWaiting); _mainGrid = (Grid)TreeHelper.TemplateFindName("PART_MainGrid", this); _mainGrid.MouseEnter -= MainGrid_MouseEnter; _mainGrid.MouseEnter += MainGrid_MouseEnter; _mainGrid.MouseMove -= MainGrid_MouseMove; _mainGrid.MouseMove += MainGrid_MouseMove; _mainGrid.MouseDown -= _mainGrid_MouseDown; _mainGrid.MouseDown += _mainGrid_MouseDown; _mainGrid.PreviewKeyDown += _mainGrid_PreviewKeyDown; _chartHeaderStackPanel = (StackPanel)TreeHelper.TemplateFindName("PART_HeaderStackPanel", this); _drawingToolStackPanel = (StackPanel)TreeHelper.TemplateFindName("PART_DrawingToolStackPanel", this); _chartPanesGrid = (Grid)TreeHelper.TemplateFindName("PART_ChartPanesGrid", this); _dateChartSliderStackPanel = (StackPanel)TreeHelper.TemplateFindName("PART_DateChartSliderStackPanel", this); _crossHairGrid = (Canvas)TreeHelper.TemplateFindName("PART_CrossHairGrid", this); _crossHairGrid.LostMouseCapture -= CrossHairGrid_LostMouseCapture; _crossHairGrid.LostMouseCapture += CrossHairGrid_LostMouseCapture; _crossHairGrid.MouseEnter -= CrossHairGrid_MouseEnter; _crossHairGrid.MouseEnter += CrossHairGrid_MouseEnter; _crossHairGrid.MouseLeave -= CrossHairGrid_MouseLeave; _crossHairGrid.MouseLeave += CrossHairGrid_MouseLeave; _crossHairGrid.MouseLeftButtonDown -= CrossHairGrid_MouseLeftButtonDown; _crossHairGrid.MouseLeftButtonDown += CrossHairGrid_MouseLeftButtonDown; _crossHairGrid.MouseLeftButtonUp -= CrossHairGrid_MouseLeftButtonUp; _crossHairGrid.MouseLeftButtonUp += CrossHairGrid_MouseLeftButtonUp; _crossHairGrid.MouseMove -= CrossHairGrid_MouseMove; _crossHairGrid.MouseMove += CrossHairGrid_MouseMove; _pointerPopup = (Popup)TreeHelper.TemplateFindName("PART_PointerPopup", this); _pointerGrid = (Grid)TreeHelper.TemplateFindName("PART_PointerGrid", this); } void _mainGrid_PreviewKeyDown(object sender, KeyEventArgs e) { //鼠标上键 if (Keyboard.IsKeyDown(Key.Down)) { this._crossHairGrid_IsDoubleClick = false; this.HiddenCrossHairGrid(); if (this.RecordCount + this.LeftRightKeyToAcceleration < this.MaxRecordCount) { this.RecordCount += this.LeftRightKeyToAcceleration; } else { this.RecordCount = this.MaxRecordCount; } } else if (Keyboard.IsKeyDown(Key.Up)) //鼠标下键 { this._crossHairGrid_IsDoubleClick = false; this.HiddenCrossHairGrid(); if (this.RecordCount - this.LeftRightKeyToAcceleration > this.MinRecordCount) { this.RecordCount -= this.LeftRightKeyToAcceleration; } else { this.RecordCount = this.MinRecordCount; } } else if (Keyboard.IsKeyDown(Key.Left)) //鼠标左键 { Mouse.OverrideCursor = Cursors.None; this.DrawingCross(false); } else if (Keyboard.IsKeyDown(Key.Right)) //鼠标右键 { Mouse.OverrideCursor = Cursors.None; this.DrawingCross(true); } else if (Keyboard.IsKeyDown(Key.Escape)) { this._crossHairGrid_IsDoubleClick = false; this.HiddenCrossHairGrid(); Mouse.OverrideCursor = Cursors.Arrow; } e.Handled = true; } private void _mainGrid_MouseDown(object sender, MouseButtonEventArgs e) { _mainGrid.Focusable = true; FocusManager.SetFocusedElement(_mainGrid, _mainGrid); } /// /// 获取和设置the root panel /// 代替PopupManager.PopupRoot,一个应用程序多图表时,popup只针对当前图表 /// internal Panel RootPanel { get { return _mainGrid; } } /// /// 计算历史周期涨幅 /// /// IList{ILineDataPoint}. private List CaclulateCycleDataIncrease(List source) { return source; } /// /// 计算分时数据涨幅 /// /// The source. private void CaclulateTikDataIncrease(IList source) { if (source == null || !source.Any()) return; foreach (var lineDataPoint in source) { var current = lineDataPoint as IOHLCDataPoint; if (current == null) continue; current.Increase = CurrentGoods.PreClose.Equals(0) ? 0f : (current.Close - CurrentGoods.PreClose) / CurrentGoods.PreClose; current.IncreaseValue = CurrentGoods.PreClose.Equals(0) ? 0f : (current.Close - CurrentGoods.PreClose); } } /// /// 清除报价牌 /// private void CleanQuoteBoard() { bool flag = _popup != null; if (flag) { _popup.Child = (null); _chartPanesGrid.Children.Remove(_popup); _popup = null; } } private void CrossHairGrid_LostMouseCapture(object sender, MouseEventArgs e) { if (CurrentGoods == null || string.IsNullOrWhiteSpace(CurrentGoods.Symbol)) return; if (_crossFlag) { if (PointerType != PointerType.Pan) { _crossHairGrid.Children.Clear(); var panes = AllPanes(); foreach (var chartPane in panes) { var layers = chartPane.AllScaleLayers; if (layers == null || !layers.Any()) continue; foreach (var scaleLayer in layers) { var left = scaleLayer.GetLeftValueScaleDisplay(); var right = scaleLayer.GetRightValueScaleDisplay(); if (left != null) left.BuildYList(); if (right != null) right.BuildYList(); } } } _crossFlag = false; _pointerPopup.IsOpen = false; if (_mainGrid.Children.Contains(_pointerPopup)) { _mainGrid.Children.Remove(_pointerPopup); } } } private void CrossHairGrid_MouseEnter(object sender, MouseEventArgs e) { if (CurrentGoods == null || string.IsNullOrWhiteSpace(CurrentGoods.Symbol)) return; SetCursorImagePos(true, e); DrawingToolExecute(); } private void CrossHairGrid_MouseLeave(object sender, MouseEventArgs e) { if (CurrentGoods == null || string.IsNullOrWhiteSpace(CurrentGoods.Symbol)) return; SetCursorImagePos(false, e); } private int _crossHairGrid_ClickTik = 0; public bool _crossHairGrid_IsDoubleClick = false; private void CrossHairGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { _crossHairGrid_ClickTik += 1; DispatcherTimer timer = new DispatcherTimer(); timer.Interval = new TimeSpan(0, 0, 0, 0, 300); timer.Tick += (cs, ce) => { timer.IsEnabled = false; _crossHairGrid_ClickTik = 0; }; timer.IsEnabled = true; if (_crossHairGrid_ClickTik % 2 == 0) { timer.IsEnabled = false; _crossHairGrid_ClickTik = 0; _crossHairGrid_IsDoubleClick = !_crossHairGrid_IsDoubleClick; if (CurrentGoods == null || string.IsNullOrWhiteSpace(CurrentGoods.Symbol)) { return; } else { if (Keyboard.Modifiers == (ModifierKeys)2 && _oldDrawingTool != null) { SetDrawingTool(_oldDrawingTool); var panes = AllPanes(); foreach (var current in panes) { Point position = e.GetPosition(current.PaintArea); if (position.X >= 0.0 && position.Y >= 0.0 && position.X <= current.PaintArea.ActualWidth && position.Y <= current.PaintArea.ActualHeight) { current.SimulateDrawingMouseDown(RuntimeHelpers.GetObjectValue(sender), e); break; } } } else { _crossHairGrid.CaptureMouse(); _crossFlag = true; if (PointerType == PointerType.Pan) { _crossX = (float)e.GetPosition(_crossHairGrid).X; _crossDateTime = DateScaler.DateFromX(_crossX); } else { if (PointerType == PointerType.CursorWithData) { _pointerPopup.IsOpen = true; } if (_crossHairGrid.Children.Count == 0) { _crossHairGrid.Children.Add(_hline); _crossHairGrid.Children.Add(_vline); } DrawingCross(e); } } } } } private void CrossHairGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (CurrentGoods == null || string.IsNullOrWhiteSpace(CurrentGoods.Symbol)) return; if (_crossFlag) { if (!_crossHairGrid_IsDoubleClick) { _crossHairGrid.ReleaseMouseCapture(); if (PointerType == PointerType.Pan) { MoveChartPane(e); } else { _crossHairGrid.Children.Clear(); var panes = AllPanes(); var scaleLayerList = panes.Select(chartPane => chartPane.AllScaleLayers) .Where(layers => layers != null && layers.Any()) .SelectMany(layers => layers) .ToList(); foreach (var scaleLayer in scaleLayerList) { scaleLayer.GetRightValueScaleDisplay().BuildYList(); scaleLayer.GetLeftValueScaleDisplay().BuildYList(); } } _crossFlag = false; _pointerPopup.IsOpen = false; } } } public void HiddenCrossHairGrid() { _crossHairGrid.ReleaseMouseCapture(); _pointerPopup.IsOpen = false; } DateTime LastRedrawTime = DateTime.Now; private Point lastPoint = new Point(); private void CrossHairGrid_MouseMove(object sender, MouseEventArgs e) { Mouse.OverrideCursor = Cursors.Arrow; if (CurrentGoods == null || string.IsNullOrWhiteSpace(CurrentGoods.Symbol)) { return; } if (_crossHairGrid_IsDoubleClick) { this.Cursor = Cursors.None; } else { this.Cursor = Cursors.Arrow; } SetCursorImagePos(true, e); if (_crossFlag) { if (PointerType == PointerType.Pan) { MoveChartPane(e); } else { DrawingCross(e); } } } /// /// Handles the Click event of the _downloadMoreButton control. /// /// The source of the event. /// The instance containing the event data. private void DownloadMoreButton_Click(object sender, RoutedEventArgs e) { _downloadMoreButton.Visibility = Visibility.Collapsed; DownloadNextDataBlock(); } private void DownloadMoreButton_MouseEnter(object sender, MouseEventArgs e) { _downloadMoreButton.ToolTip = string.Format(LanguageManager.FindResource(LanguageConst.ToolTip_DownloadMore), MaxLoadRecordCount); } /// /// 更新图表 /// private void DrawingChart(bool isUpdataSet) { if (_chartPanesGrid == null) return; if (CycleType == CycleType.TimeSharing) { DrawingTimeShareChart(); return; //分时图不使用这个方法更新图表 } var allDateScaler = (AllDateScaler)DateScaler; var width = (int)GetChartPaneWidth(); width = Math.Max(0, width); var left = (float)GetChartPanelOffset(); allDateScaler.SetPlotInfo(width, left); _dateScaleDisplay.BuildCanvas(allDateScaler, left, width, false); //DateChartSlider.Margin = new Thickness(left, 0, 0, 0); _downloadMoreButtonPlaceHolder.Width = left; DateChartSlider.BuildDateChartSlider(); ShowDownloadButton(allDateScaler.StartDateIndex == 0); var chartPanes = _chartPanesGrid.Children.OfType(); var childrenCount = chartPanes.Count(); var paneIndex = 0; if (CycleType == CycleType.Tik) { foreach (var current in _chartPanesGrid.Children) { var pane = current as ChartPane; if (pane != null) { //最后一个ChartPane时更新 pane.RefreshChartFlag = true; pane.BuildTimeShareChartPane(allDateScaler, _dateScaleDisplay); } } } else { foreach (var current in chartPanes) { paneIndex++; if (current != null) { //最后一个ChartPane时更新 current.RefreshChartFlag = paneIndex == childrenCount; current.BuildChartPane(allDateScaler, _dateScaleDisplay, isUpdataSet); } } } } /// /// 创建分时图 /// private void DrawingTimeShareChart() { if (_chartPanesGrid == null) return; var width = (int)GetChartPaneWidth(); width = Math.Max(0, width); var left = (float)GetChartPanelOffset(); var allDateScaler = CreateDateScaler(width, left); DateScaler = allDateScaler; _dateScaleDisplay.BuildTimeShareCanvas(allDateScaler, left, width, false, CurrentGoods.OpenCloseTimes, this); int paneIndex = 0; foreach (var current in _chartPanesGrid.Children) { var pane = current as ChartPane; if (pane != null) { //最后一个ChartPane时更新 pane.RefreshChartFlag = paneIndex == 1; pane.BuildTimeShareChartPane(allDateScaler, _dateScaleDisplay); paneIndex++; } } } /// /// 创建分时图的时间轴 /// private AllDateScaler CreateDateScaler(int width, float left) { int count = SetCountPoint(); var dateScaler = new AllDateScaler(this) { NumBars = count, DesiredEndDate = DateTime.Today.AddMinutes(count), }; dateScaler.SetPlotInfo(width, left); var dataPoints = new List(); if (CurrentGoods == null || CurrentGoods.OpenCloseTimes == null) return dateScaler; var timeSpan = TimeSpan.Zero; foreach (var openCloseTimes in CurrentGoods.OpenCloseTimes) { timeSpan = openCloseTimes.CloseTime - openCloseTimes.OpenTime; for (int i = 0; i <= timeSpan.TotalMinutes; i++) { dataPoints.Add(new BarDataPoint(openCloseTimes.OpenTime.AddMinutes(i), 0)); } } dateScaler.SetDates(dataPoints, CurrentTimeFrame, false); return dateScaler; } /// /// 十字光标按下时画交叉线 /// /// The instance containing the event data. private void DrawingCross(MouseEventArgs e) { var position = e.GetPosition(_crossHairGrid); _currentX = position.X; _currentY = position.Y; currentX = _currentX; currentY = _currentY; _offSetNum = 0; //限制超出ChartPane时十字光标的显示-新增OriginalDateValue, OriginalDateFromX var start = DateScaler.OriginalDateValue(DateScaler.StartDateIndex); var end = DateScaler.OriginalDateValue(DateScaler.EndDateIndex); var distance = DateScaler.DistanceBetweenDates(); _currentX -= (float)distance / 2; var aDate = (_currentX <= 0 || _currentX < distance) ? start : DateScaler.OriginalDateFromX(_currentX - 5); if (aDate.Equals(DateTime.MinValue) || aDate > end) { aDate = end; } if (aDate.Equals(DateTime.MinValue) || aDate < start) { aDate = start; } //显示报价牌 ShowQuoteBoard(aDate); var num = (float)((int)Math.Round((double)DateScaler.XforDate(aDate))); //5.0f 是PART_ChartPanesGrid的Margin, 0.5f在LinePlot/DateScaleDisplay里都加有0.5的微调 //另外加上左边纵坐标引起的位移 num = num + 5f + 0.5f + (float)GetChartPanelOffset(); var y = _currentY < 0 ? 0 : _currentY; var with = _crossHairGrid.ActualWidth; var height = _crossHairGrid.ActualHeight; var brush = IsDarkBackground ? (new SolidColorBrush(Colors.White)) : (new SolidColorBrush(Colors.Black)); _hline.X1 = 0; _hline.X2 = with; _hline.Y1 = y; _hline.Y2 = y; _hline.Stroke = brush; _vline.X1 = num; _vline.X2 = num; _vline.Y1 = 0; _vline.Y2 = height; _vline.Stroke = brush; var panes = AllPanes(); foreach (var current in panes) { var position2 = e.GetPosition(current); bool flag = position2.Y >= 0.0 && position2.Y <= current.ActualHeight; var layers = current.AllScaleLayers; if (layers == null || !layers.Any()) continue; foreach (var scaleLayer in layers) { var left = scaleLayer.GetLeftValueScaleDisplay(); var right = scaleLayer.GetRightValueScaleDisplay(); if (flag) { if (left != null) left.BuildYList((float)position2.Y); if (right != null) right.BuildYList((float)position2.Y); } else { if (left != null) left.BuildYList(); if (right != null) right.BuildYList(); } } } } /// /// 十字光标按下时画交叉线 /// /// The instance containing the event data. public void DrawingCross(bool isAddTime = true) { _offSetNum += isAddTime ? 1 : -1; var aDate = DateScaler.OriginalDateFromX(currentX - 5, _offSetNum); if (DateScaler.XforDate(aDate) < 0) { _offSetNum += (!isAddTime) ? 1 : -1; return; } if (DateScaler.XforDate(aDate) > _chartPanesGrid.ActualWidth) { _offSetNum += (!isAddTime) ? 1 : -1; return; } //显示报价牌 ShowQuoteBoard(aDate); var num = (float)((int)Math.Round((double)DateScaler.XforDate(aDate))); //5.0f 是PART_ChartPanesGrid的Margin, 0.5f在LinePlot/DateScaleDisplay里都加有0.5的微调 //另外加上左边纵坐标引起的位移 num = num + 5f + 0.5f + (float)GetChartPanelOffset(); var y = _currentY < 0 ? 0 : _currentY; var with = _crossHairGrid.ActualWidth; var height = _crossHairGrid.ActualHeight; var brush = IsDarkBackground ? (new SolidColorBrush(Colors.White)) : (new SolidColorBrush(Colors.Black)); _hline.X1 = 0; _hline.X2 = with; _hline.Y1 = y; _hline.Y2 = y; _hline.Stroke = brush; _vline.X1 = num; _vline.X2 = num; _vline.Y1 = 0; _vline.Y2 = height; _vline.Stroke = brush; var panes = AllPanes(); foreach (var current in panes) { bool flag = currentY >= 0.0 && currentY <= current.ActualHeight; var layers = current.AllScaleLayers; if (layers == null || !layers.Any()) continue; foreach (var scaleLayer in layers) { var left = scaleLayer.GetLeftValueScaleDisplay(); var right = scaleLayer.GetRightValueScaleDisplay(); if (flag) { if (left != null) left.BuildYList((float)currentY); if (right != null) right.BuildYList((float)currentY); } else { if (left != null) left.BuildYList(); if (right != null) right.BuildYList(); } } } } /// /// 加载叠加商品数据 /// /// The comparison goods. /// IList{ILineDataPoint}. private List LoadComparisonDataSet(ComparisonGoods comparisonGoods) { //历史数据回补后叠加商品记录条数以当前主商品记录条数为准 var flag = DataSet == null || DataSet.DataPoints == null || !DataSet.DataPoints.Any(); if (flag) return null; var count = DataSet.DataPoints.Count; var endTime = DataSet.DataPoints.Max(z => z.Date); return OnLoadHistoryCycleData(comparisonGoods, CycleType, CurrentTimeFrame, MaxLoadRecordCount, endTime); } /// /// Loads the configs from XML. /// /// Name of the config. private void LoadConfigsFromXml(string configName) { //to-do: } /// /// Handles the MouseEnter event of the MainGrid control. /// /// The source of the event. /// The instance containing the event data. private void MainGrid_MouseEnter(object sender, MouseEventArgs e) { if (CurrentDrawingTool != null) { SetDrawingTool(CurrentDrawingTool); } else { DrawingToolExecute(); } } /// /// Handles the MouseMove event of the MainGrid control. /// /// The source of the event. /// The instance containing the event data. private void MainGrid_MouseMove(object sender, MouseEventArgs e) { if (CurrentDrawingTool != null) { SetDrawingTool(CurrentDrawingTool); } else { DrawingToolExecute(); } //Console.WriteLine("鼠标移动开始:" + startTime); //timer.IsEnabled = false; //startTime = DateTime.Now; //timer.IsEnabled = true; } /// /// Pane工具相当于移动时间滑块 /// /// The instance containing the event data. private void MoveChartPane(MouseEventArgs e) { DateTime desiredEndDate = DateScaler.DesiredEndDate; float num = DateScaler.XforDate(_crossDateTime); var num2 = (float)(_crossHairGrid.ActualWidth / (double)DateScaler.NumBars); var num3 = (float)(e.GetPosition(_crossHairGrid).X - (double)num); var num4 = checked((int)Math.Round((double)unchecked((float)DateScaler.EndDateIndex - num3 / num2))); var count = DateScaler.Count(); bool flag = num4 >= count; DateScaler.DesiredEndDate = flag ? DateTime.MaxValue : DateScaler.DateValue(num4); if (!DateScaler.DesiredEndDate.Equals(desiredEndDate)) { Refresh(); } } /// /// 清除图表容器,切换周期时使用 /// private void ChangePanesClear() { _chartPanes.Clear(); _chartPanesGrid.Children.Clear(); _chartPanesGrid.RowDefinitions.Clear(); } /// /// Called when [cycle type changed]. /// /// if set to true [refresh now]. /// 是否是从分时图中切换 private void OnCycleTypeChanged(bool refreshNow, bool isFromTimesharing = false) { if (CurrentGoods == null) return; var cycleType = CycleType; var timeSpan = CurrentTimeFrame; var goods = CurrentGoods; var maxLoadRecordCount = MaxLoadRecordCount; Dispatcher.BeginInvoke(new Action(() => { DataSet = new ChartDataSet(); switch (cycleType) { case CycleType.TimeSharing: ChangePanesClear(); DateChartSlider.Visibility = Visibility.Collapsed; _downloadMoreButton.Visibility = Visibility.Collapsed; OpenTimeSharePlot(CurrentGoods); _timeSharingTimer.Start(); //开始定时器补数据 SetDatesAndRefresh(refreshNow); break; case CycleType.Tik: ChangePanesClear(); // DateChartSlider.Visibility = Visibility.Collapsed; DateChartSlider.Visibility = Visibility.Visible; _downloadMoreButton.Visibility = Visibility.Collapsed; _timeSharingTimer.Stop(); CreateTikChart(CurrentGoods); SetDatesAndRefresh(refreshNow); break; default: ////数据源更改,计算涨幅 if (isFromTimesharing) { ChangePanesClear(); foreach (var chartPane in _chartCachePanes) { //取最大grid高度做* var stars = _chartPanesGrid.RowDefinitions.Count > 0 ? _chartPanesGrid.RowDefinitions.Max(z => z.Height.Value) : 1.0; AddChartPane(chartPane, stars, IsMainChartPane(chartPane)); } } //CreateMinuteChart(CurrentGoods); _timeSharingTimer.Stop(); DateChartSlider.Visibility = Visibility.Visible; DataSet.DataPoints = CaclulateCycleDataIncrease(OnLoadHistoryCycleData(goods, cycleType, timeSpan, maxLoadRecordCount, DateTime.Now)); SetDatesAndRefresh(refreshNow); break; } }), DispatcherPriority.Background); } /// /// 设置日期和刷新. /// /// if set to true [refresh now]. private void SetDatesAndRefresh(bool refreshNow,bool isupdateBar=true) { Dispatcher.BeginInvoke(new Action(() => { var allDateScaler = (AllDateScaler)DateScaler; allDateScaler.SetDates(DataSet.DataPoints, CurrentTimeFrame, false); //numBars为矩形柱间距,矩形柱数量实际要-1 if (isupdateBar) { allDateScaler.NumBars = RecordCountInternal - 1; } // if (refreshNow) Refresh(true); if (DataSet.DataPoints != null && DataSet.DataPoints.Any()) { Refresh(refreshNow); } }), DispatcherPriority.Background); } /// /// Cycles the type property changed. /// /// 是否是从分时图中切换 private void CycleTypePropertyChanged(bool isFromTimesharing) { Dispatcher.BeginInvoke(new Action(() => { if (CycleType.TimeSharing == CycleType) { _chartCachePanes.Clear(); foreach (var chartPane in _chartPanes) { if (!chartPane.IsTimeSpaning) { _chartCachePanes.Add(chartPane); } } } if (_comparisonGoodsList == null || !_comparisonGoodsList.Any()) { OnCycleTypeChanged(true, isFromTimesharing); } else { OnCycleTypeChanged(false, isFromTimesharing); //刷新叠加商品 RefreshComparsionGoods(); } //更新底部快捷周期状态 // TimeSpanPicker.UpdateCyleType(CycleType); }), DispatcherPriority.Background); } /// /// Called when [load goods data]. /// /// The goods. /// The total count. /// The end time. /// ChartDataSet. private List OnLoadHistoryCycleData(GoodsInfo goods, CycleType cycleType, TimeSpan timeSpan, int totalCount, DateTime endTime) { List result = null; var cycleTypeHandler = LoadHistoryCycleDataByCycleType; if (cycleTypeHandler != null) { result = cycleTypeHandler(goods.ExchangeCode, goods.GoodsCode, cycleType, timeSpan, DateTime.MinValue, endTime, totalCount); } //集合都要按时间顺排序,才能画图 return result == null ? null : result.OrderBy(z => z.Date).ToList(); } /// /// 获取分时图的数据分钟数据 /// /// The goods. /// The total count. /// 开始时间 /// The end time. /// ChartDataSet. private List OnLoadHistoryTimeSpaningCycleData(GoodsInfo goods, int totalCount, DateTime startTime, DateTime endTime) { List result = null; var cycleTypeHandler = LoadHistoryCycleDataByCycleType; if (cycleTypeHandler != null) { result = cycleTypeHandler(goods.ExchangeCode, goods.GoodsCode, CycleType.Minute, CurrentTimeFrame, startTime, endTime, totalCount); } //集合都要按时间顺排序,才能画图 return result == null ? null : result.OrderBy(z => z.Date).ToList(); } /// /// 更改时间间隔后执行 /// /// Type of the cycle. private void OnTimeSpanChanged(CycleType cycleType) { DateScaler.DesiredEndDate = DateTime.MaxValue; //必须设为最大值 CycleType = cycleType; } /// /// 设置当前画线工具,并处理它与图例间的变动 /// /// private void SetDrawingTool(IDrawingTool aDrawing) { CurrentDrawingTool = aDrawing; DrawingToolExecute(); var chartPanes = AllPanes(); foreach (var chartPane in chartPanes) { chartPane.OnDrawing(); } } /// /// 显示当前时间的报价牌 /// /// A date. private void ShowQuoteBoard(DateTime aDate) { if (_pointerPopup.IsOpen && !aDate.Equals(DateTime.MinValue)) { var list = new List(); ControlPair controlPair = null; var controlPair2 = new ControlPair(); var textBlock = new TextBlock { Text = aDate.ToString("yyyy-MM-dd ddd HH:mm:ss"), //aDate.ToString(aDate.TimeOfDay.Ticks == 0L ? "yyyy-MM-dd ddd" : "yyyy-MM-dd ddd hh:mm:ss"), Foreground = IsDarkBackground ? (new SolidColorBrush(Colors.White)) : (new SolidColorBrush(Colors.Black)), HorizontalAlignment = HorizontalAlignment.Center }; controlPair2.ControlLeft = textBlock; list.Add(controlPair2); var panes = AllPanes(); foreach (var current in panes) { bool flag = false; var scaleLayers = current.AllScaleLayers; if (scaleLayers != null && scaleLayers.Any()) { foreach (var scaleLayer in scaleLayers) { var allFormulas = new List(scaleLayer.AllDisplayFormula); allFormulas.AddRange(scaleLayer.ComparisonFormulas); if (!allFormulas.Any()) continue; foreach (var formula in allFormulas) { //plot.PlotBase_5031(aDate, IsDarkBackground, SymbolManager.Manager().SymbolPrecision(CurrentSymbol) > 2) var controlPairs = formula.GetControlPairs(aDate, IsDarkBackground, this.CurrentGoods.MinUnit); if (controlPairs != null && controlPairs.Any()) { flag = true; if (controlPair != null) { list.Add(controlPair); controlPair = null; } list.AddRange(controlPairs); } } } } if (flag) { controlPair = new ControlPair() { ControlLeft = new Border() { Margin = new Thickness(0.0, 2.0, 0.0, 2.0), Height = 2.0, BorderThickness = new Thickness(1.0), BorderBrush = new SolidColorBrush(ColorHelper.DimGray) } }; } } _pointerGrid.RowDefinitions.Clear(); _pointerGrid.Children.Clear(); int num = 0; foreach (var current in list) { var rowDefinition = new RowDefinition { Height = new GridLength(1.0, 0) }; _pointerGrid.RowDefinitions.Add(rowDefinition); if (current.ControlLeft != null) { current.ControlLeft.SetValue(Control.FontSizeProperty, ChartFontSize); if (current.ControlRight == null) { current.ControlLeft.SetValue(Grid.RowProperty, num); current.ControlLeft.SetValue(Grid.ColumnSpanProperty, 2); _pointerGrid.Children.Add(current.ControlLeft); } else { current.ControlRight.SetValue(Control.FontSizeProperty, ChartFontSize); current.ControlLeft.SetValue(Grid.RowProperty, num); current.ControlRight.SetValue(Grid.RowProperty, num); current.ControlRight.SetValue(Grid.ColumnProperty, 1); _pointerGrid.Children.Add(current.ControlLeft); _pointerGrid.Children.Add(current.ControlRight); } } num++; } //_pointerGrid.UpdateLayout(); //左边纵坐标宽度 var buffer = GetChartPanelOffset(); if (_pointerGrid.ActualWidth + buffer > (double)_currentX) { _pointerPopup.HorizontalOffset = (_pointerGrid.ActualWidth + buffer); } else { _pointerPopup.HorizontalOffset = buffer; } } } #endregion Private Methods #region 分时图 private void _timeSharingTimer_Tick(object sender, EventArgs e) { //todo:修改使用行情时间 var updateTime = _goodsQuoteTime.AddMinutes(1); //var updateTime = DateTime.Now.AddSeconds(-DateTime.Now.Second); if (DataSet == null || DataSet.DataPoints == null || !DataSet.DataPoints.Any()) { //没有数据源无法补数据 _timeSharingTimer.Stop(); } else { var lastPoint = DataSet.DataPoints[DataSet.DataPoints.Count - 1]; var dataPoint = new BarDataPoint(lastPoint.Date, lastPoint.Value); ; var timeSpan = updateTime - dataPoint.Date; if (timeSpan.TotalSeconds >= 60) { dataPoint.Date = updateTime; dataPoint.Volume = 0; UpdateTimeShareData(dataPoint, this.CurrentGoods.GoodsCode); } UpdateTimerComparisonShareing(updateTime); //定时器更新叠加数据 } } /// /// 定时器更新叠加商品数据 /// /// 更新时间 private void UpdateTimerComparisonShareing(DateTime updateTime) { if (this.ComparisionTimeShareDic != null) { var keyList = this.ComparisionTimeShareDic.Keys.ToList(); foreach (var goodsCodeKey in keyList) { if (this.ComparisionTimeShareDic[goodsCodeKey].Any()) { var lastPoint = this.ComparisionTimeShareDic[goodsCodeKey][ this.ComparisionTimeShareDic[goodsCodeKey].Count - 1]; var dataPoint = new BarDataPoint(lastPoint.Date, lastPoint.Value); ; var timeSpan = updateTime - dataPoint.Date; if (timeSpan.TotalSeconds >= 60) { dataPoint.Date = updateTime; UpdateComparisonData(goodsCodeKey, (IBarDataPoint)dataPoint); } } } } } /// /// 更新实时数据数据集 /// /// 新添加的数据 /// 更新数据的商品代码 public void UpdateTimeShareData(IBarDataPoint barDataPoint, string goodsCode) { if (barDataPoint == null) return; //格式化时间 barDataPoint.Date = barDataPoint.Date.Date.AddMinutes((int)barDataPoint.Date.TimeOfDay.TotalMinutes); this.Dispatcher.BeginInvoke(new Action(() => { if (this.CurrentGoods != null && goodsCode == this.CurrentGoods.GoodsCode) { if (DataSet == null) { DataSet = new ChartDataSet(); } if (DataSet.DataPoints == null) { DataSet.DataPoints = new List(); } if (DataSet.DataPoints.Any()) { var volData = DataSet.DataPoints[DataSet.DataPoints.Count - 1]; var timeSpan = barDataPoint.Date.AddSeconds(-barDataPoint.Date.Second) - volData.Date.AddSeconds(-barDataPoint.Date.Second); int count = (int)timeSpan.TotalMinutes; if (count < 0) { return; } if (count == 0) { DataSet.DataPoints[DataSet.DataPoints.Count - 1] = barDataPoint; } else { if (count >= 2 && count <= 1440) //使用定时器时更新可能不会出现这情况 { var repairPoint = DataSet.DataPoints[DataSet.DataPoints.Count - 1]; var repairBarPoint = repairPoint as IBarDataPoint; if (repairBarPoint != null) { repairBarPoint.Volume = 0; } for (int i = 0; i < count - 1; i++) { DataSet.DataPoints.Add(repairBarPoint); } } DataSet.DataPoints.Add(barDataPoint); } } else { DataSet.DataPoints.Add(barDataPoint); } if (timeSharingFormulaModel != null) { timeSharingFormulaModel.UpdatePointSharing(barDataPoint); } } else { UpdateComparisonData(goodsCode, barDataPoint); } Refresh(true); })); } /// /// 更新叠加商品分时图 /// /// 商品代码 /// private void UpdateComparisonData(string goodsCode, IBarDataPoint barDataPoint) { //更新叠加分时图数据 if (ComparisionTimeShareDic.ContainsKey(goodsCode)) { barDataPoint.Date = barDataPoint.Date.Date.AddMinutes((int)barDataPoint.Date.TimeOfDay.TotalMinutes); ComparisionTimeShareDic[goodsCode] = ComparisionTimeShareDic[goodsCode] ?? new List(); if (ComparisionTimeShareDic[goodsCode].Any()) { var volData = ComparisionTimeShareDic[goodsCode][ComparisionTimeShareDic[goodsCode].Count - 1]; var timeSpan = barDataPoint.Date.AddSeconds(-barDataPoint.Date.Second) - volData.Date.AddSeconds(-barDataPoint.Date.Second); int count = (int)timeSpan.TotalMinutes; if (count < 0) { return; } else if (count == 0) { ComparisionTimeShareDic[goodsCode][ComparisionTimeShareDic[goodsCode].Count - 1] = barDataPoint; } else { if (count >= 2 && count <= 1440) //使用定时器时更新可能不会出现这情况 { var repairPoint = ComparisionTimeShareDic[goodsCode][ComparisionTimeShareDic[goodsCode].Count - 1]; for (int i = 0; i < count - 1; i++) { ComparisionTimeShareDic[goodsCode].Add(repairPoint); } } ComparisionTimeShareDic[goodsCode].Add(barDataPoint); } } else { ComparisionTimeShareDic[goodsCode].Add(barDataPoint); } if (this._comparisonFommulaHsSet != null && _comparisonFommulaHsSet.ContainsKey(goodsCode)) { _comparisonFommulaHsSet[goodsCode].UpdateComparsionPoint(barDataPoint); } } } /// /// 更新单笔数据 /// /// tik数据 /// public void UpdateTikData(IBarDataPoint barDataPoint, string goodsCode) { if (barDataPoint == null) return; if (this.CurrentGoods != null && goodsCode == this.CurrentGoods.GoodsCode) { if (DataSet == null) { DataSet = new ChartDataSet(); } if (DataSet.DataPoints == null) { DataSet.DataPoints = new List(); } DataSet.DataPoints.Add(barDataPoint); if (_tikFormulaModel != null) { _tikFormulaModel.UdpdateTickPoint(barDataPoint); Refresh(true); } } } /// /// 更新单笔数据 /// public void UpdateMinuteData(IBarDataPoint barDataPoint, string goodsCode) { DataSet.DataPoints = CaclulateCycleDataIncrease(OnLoadHistoryCycleData(CurrentGoods, CycleType, CurrentTimeFrame, MaxLoadRecordCount, DateTime.Now)); SetDatesAndRefresh(true); } /// /// 设置分时图当前点的数量 /// /// 当前数量 private int SetCountPoint() { if (CurrentGoods == null || CurrentGoods.OpenCloseTimes == null) return 0; var timeSpan = TimeSpan.Zero; foreach (var openCloseTime in CurrentGoods.OpenCloseTimes) { timeSpan += openCloseTime.CloseTime - openCloseTime.OpenTime; } var count = (int)timeSpan.TotalMinutes + CurrentGoods.OpenCloseTimes.Count; //加上每段时间需加1 _timeSharingMaxPointCount = count; return count; } /// /// 打开分时图 /// /// 分时图商品 public void OpenTimeSharePlot(GoodsInfo goodsInfo) { var pane = new ChartPane(this, true); //取最大grid高度做* var stars = _chartPanesGrid.RowDefinitions.Count > 0 ? _chartPanesGrid.RowDefinitions.Max(z => z.Height.Value) : 1.0; AddChartPane(pane, stars); SetCountPoint(); var getEndDatatime = DateTime.Now; var startDateTime = DateTime.Now.Date; if (CurrentGoods.OpenCloseTimes != null && CurrentGoods.OpenCloseTimes.Any()) //取当前开收市时间 { getEndDatatime = CurrentGoods.OpenCloseTimes[CurrentGoods.OpenCloseTimes.Count - 1].CloseTime; startDateTime = CurrentGoods.OpenCloseTimes[0].OpenTime; } DataSet.DataPoints = OnLoadHistoryTimeSpaningCycleData(CurrentGoods, _timeSharingMaxPointCount, startDateTime, getEndDatatime); // GetComparisonDataSet(_timeSharingMaxPointCount, startDateTime, getEndDatatime); //OnLoadHistoryTimeSpaningCycleData(CurrentGoods, _timeSharingMaxPointCount, startDateTime, getEndDatatime); var scaleLayer = new ScaleLayer(); var displayFormula = new DisplayFormula(DataSet, this, goodsInfo) { OpenTime = startDateTime, CloseTime = getEndDatatime, }; timeSharingFormulaModel = displayFormula; scaleLayer.AllDisplayFormula.Add(displayFormula); displayFormula.ScaleLayer = scaleLayer; displayFormula.IsTimeShare = true; pane.AddScaleLayer(scaleLayer); var plots = new List(); foreach (var indicator in displayFormula.FormulaModel.IndicatorList) { if (!indicator.VolBar) //是否为成交量 { var linePlot = new LinePlot(indicator, this) { ScaleLayer = scaleLayer }; scaleLayer.AllPlots.Add(linePlot); plots.Add(linePlot); } else { //成交量显示在下方 //var volScaleLayer = new ScaleLayer(); //var volpane = new ChartPane(this); ////取最大grid高度做* //var start = _chartPanesGrid.RowDefinitions.Count > 0 // ? _chartPanesGrid.RowDefinitions.Max(z => z.Height.Value) // : 1.0; //AddChartPane(volpane, start); //volpane.AddScaleLayer(volScaleLayer); //var linePlot = new LinePlot(indicator, this); //volScaleLayer.AllPlots.Add(linePlot); //linePlot.ScaleLayer = volScaleLayer; //plots.Add(linePlot); } } //添加叠加分时图 GetComparisonDataSet(_timeSharingMaxPointCount, startDateTime, getEndDatatime, scaleLayer); displayFormula.PlotBases = plots; // DrawingTimeShareChart(); } /// /// 显示即时图 /// /// 商品 public void CreateTikChart(GoodsInfo goodsInfo) { var pane = new ChartPane(this); //取最大grid高度做* var stars = _chartPanesGrid.RowDefinitions.Count > 0 ? _chartPanesGrid.RowDefinitions.Max(z => z.Height.Value) : 1.0; AddChartPane(pane, stars); var getEndDatatime = DateTime.Now; var startDateTime = DateTime.Now.Date; if (CurrentGoods.OpenCloseTimes != null && CurrentGoods.OpenCloseTimes.Any()) //取当前开收市时间 { getEndDatatime = CurrentGoods.OpenCloseTimes[CurrentGoods.OpenCloseTimes.Count - 1].CloseTime; startDateTime = CurrentGoods.OpenCloseTimes[0].OpenTime; } DataSet.DataPoints = OnLoadHistoryTimeSpaningCycleData(CurrentGoods, _timeSharingMaxPointCount, startDateTime, getEndDatatime); var scaleLayer = new ScaleLayer(); var formula = DisplayFormula.CreateTikChartFormula(this, goodsInfo, DataSet.DataPoints); scaleLayer.AllDisplayFormula.Add(formula); formula.ScaleLayer = scaleLayer; pane.AddScaleLayer(scaleLayer); _tikFormulaModel = formula; var plots = new List(); foreach (var indicator in formula.FormulaModel.IndicatorList) { var linePlot = new LinePlot(indicator, this) { ScaleLayer = scaleLayer }; scaleLayer.AllPlots.Add(linePlot); plots.Add(linePlot); } formula.PlotBases = plots; } /// /// 获取叠加商品数据 /// /// 取数据量 /// 开始时间 /// 结束时间 private void GetComparisonDataSet(int dataCount, DateTime startTime, DateTime endDateTime, ScaleLayer comparisonLayer) { ComparisionTimeShareDic.Clear(); _comparisonFommulaHsSet.Clear(); if (_comparisonGoodsList == null) return; foreach (var goodseItem in _comparisonGoodsList) { var lineDataList = OnLoadHistoryTimeSpaningCycleData(goodseItem, dataCount, startTime, endDateTime); ComparisionTimeShareDic[goodseItem.GoodsCode] = lineDataList; } var formulas = DisplayFormula.TimeSharingFormulas(_comparisonGoodsList, this, ComparisionTimeShareDic); foreach (var displayFormula in formulas) { CreateComparisonPlot(displayFormula, comparisonLayer); } } /// ///创建图形,添加到层中 /// /// 公式模型 /// 公式层 private void CreateComparisonPlot(DisplayFormula displayFormula, ScaleLayer comparisonLayer) { displayFormula.PlotBases = new List(); _comparisonFommulaHsSet[displayFormula.ComparionGoodsInfo.GoodsCode] = displayFormula; comparisonLayer.ComparisonFormulas.Add(displayFormula); foreach (var indicator in displayFormula.FormulaModel.IndicatorList) { var linePlot = new ComparisonPlot(indicator, this, displayFormula.ComparionGoodsInfo) { ScaleLayer = comparisonLayer }; linePlot.IsActive = true; comparisonLayer.AllPlots.Add(linePlot); displayFormula.PlotBases.Add(linePlot); } } #endregion #region 指标 /// /// 设置当前公式参数 /// public void SetCurrectFormulaParam() { if (this._chartPanes != null && _chartPanes.Any()) { var scaleLayers = new List(); var displayFormulas = new List(); foreach (var chartPane in _chartPanes) { if (chartPane.AllScaleLayers != null && chartPane.AllScaleLayers.Any()) { scaleLayers.AddRange(chartPane.AllScaleLayers); } } foreach (var scaleLayer in scaleLayers) { if (scaleLayer.AllDisplayFormula != null && scaleLayer.AllDisplayFormula.Any()) { displayFormulas.AddRange(scaleLayer.AllDisplayFormula); } } if (displayFormulas.Any()) { var formulaModels = displayFormulas.Select((displayFormula) => displayFormula.FormulaModel).ToList(); var programModels = formulaModels.Select((formulaModel) => formulaModel.EditProgram).ToList(); programModels = programModels.Where((model) => model != null).ToList(); var paramsSetPage = new ParamsSetPage(programModels); var program = paramsSetPage.ShowDialog(); if (program.HasValue && program.Value == true) //更新图表 { foreach (var displayFormula in displayFormulas) //设置公式参数 { displayFormula.FormulaModel.SetParamValue(); displayFormula.Calculation(); } this.Refresh(true); } } } } /// /// 打开公式编辑器 /// public void OpenFormulEditer() { if (this._chartPanes != null && _chartPanes.Any()) { var scaleLayers = new List(); var displayFormulas = new List(); foreach (var chartPane in _chartPanes) { if (chartPane.AllScaleLayers != null && chartPane.AllScaleLayers.Any()) { scaleLayers.AddRange(chartPane.AllScaleLayers); } } foreach (var scaleLayer in scaleLayers) { if (scaleLayer.AllDisplayFormula != null && scaleLayer.AllDisplayFormula.Any()) { displayFormulas.AddRange(scaleLayer.AllDisplayFormula); } } if (displayFormulas.Any()) { var formulaModels = displayFormulas.Select((displayFormula) => displayFormula.FormulaModel).ToList(); var programModels = formulaModels.Select((formulaModel) => formulaModel.EditProgram).ToList(); programModels = programModels.Where((model) => model != null).ToList(); var vm = new EditFormulaViewModel(programModels); vm.OpenEditer(); foreach (var displayFormula in displayFormulas) //设置公式参数 { displayFormula.FormulaModel.SetParamValue(); displayFormula.Calculation(); } this.Refresh(true); } } } /// /// 清除主图中指标 /// public void ClearMainPaneIndicator() { var pane = GetMainChartPane(); if (pane != null) { foreach (var scaleLayer in pane.AllScaleLayers) { scaleLayer.ClearFormulas(); } this.Refresh(); } } /// /// 通过名称添加指标 /// /// 指标名称 /// 是否立刻刷新图表 public void AddIndicatorByName(string formulaName, bool refresh = true) { var formula = FormulaHelper.GetFormulaByName(formulaName, string.Empty); if (formula != null) { var displayFormula = new DisplayFormula(formula, DataSet, this); AddFormulaFromPane(displayFormula); displayFormula.SetSource(DataSet, false); if (refresh) { Refresh(true); } } } /// /// 添加指标 /// /// 指标配置 public void AddIndicator(ProgramModel program) { var formulaModel = program.CovertToFormulaModel(); var displayFormula = new DisplayFormula(formulaModel, DataSet, this); AddFormulaFromPane(displayFormula); displayFormula.SetSource(DataSet, false); Refresh(true); } #endregion #endregion Methods } }