using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using TradeIdeas.TIProData.Interfaces;
// This file contains a variety of miscellaneous support classes for ConfigDemo lessons.
namespace TradeIdeas.TIProGUI.CBT
{
///
/// Tab sheets can be created in other classes and files. This is what the guts knows
/// about its parent. It doesn't directly know or care about the ConfigDemo class. I
/// don't expect other classes to implement this interface, but this keeps the
/// interface simpler and better documented.
///
public interface IContainer
{
///
/// Which filter are we looking at?
///
string InternalCode { get; }
///
/// The text description of the field, like "Price ($)" or "Bid Size (Shares)".
///
string Description { get; }
IConnectionMaster ConnectionMaster { get; }
///
/// Read and parse the min value from the text box. Do it in a standard way.
///
double? MinValue { get; }
///
/// Read and parse the max value from the text box. Do it in a standard way.
///
double? MaxValue { get; }
///
/// Use this to set the value, or to listen for changes to the value.
/// is the preferred way to read this value.
///
TextBox MinTextBox { get; }
///
/// Use this to set the value, or to listen for changes to the value.
/// is the preferred way to read this value.
///
TextBox MaxTextBox { get; }
///
/// Switch to a tab page which is good at showing an example. This is mostly
/// aimed at the class. That page is good at listing
/// examples, but not at showing the results.
///
void ShowExample();
}
///
/// Each tab sheet we create should return one of these.
///
public interface IChild
{
///
/// The user friendly name to display on the tab sheet.
///
string Name { get; }
///
/// This will be added to the tab sheet.
/// Presumably the object will be returning the this pointer with a cast.
/// will probably create the
///
Control Body { get; }
///
/// After the user sets an example, is it reasonable to show the example
/// on this tab page? Ideally we would show multiple views, perhaps all
/// the views, at once. Since we can only show one at a time, what is
/// the best place to jump after someone clicks on an example.
///
bool CanShowExample { get; }
///
/// This event is fired any time the child is made visible. It would be
/// very hard to set up a traditional C# event, so instead the container
/// just calls this every time.
///
void OnShow();
}
public delegate IChild Creator(IContainer container);
public class Translator
{
public double M { get; set; }
public double B { get; set; }
public double Get(double x)
{
return M * x + B;
}
public double[] Get(double low, double high, double open, double close)
{
return new double[4] { Get(low), Get(high), Get(open), Get(close) };
}
public double[] GetSingle(double x)
{
return new double[1] { Get(x) };
}
public Translator()
{
M = 1;
B = 0;
}
private static Random _random = new Random();
///
/// This is aimed at filters where the scale doesn't matter at all.
///
public void RandomScale()
{
B = _random.Next(100) + 1;
switch (_random.Next(4))
{
case 0:
M = 1;
break;
case 1:
M = 2;
break;
case 2:
M = 5;
break;
case 3:
M = 10;
break;
}
}
}
public class MinMaxSummary
{
public Chart Chart { get; private set; }
public ChartArea ChartArea { get; private set; }
private int _x;
Series _placeHolder;
public int X
{
get { return _x; }
set
{
_x = value;
// If we try to draw this too far to the right, off the end of the chart,
// things start to act strange. So I'm creating a placeholder to make sure
// we have enough room.
if (null == _placeHolder)
{
_placeHolder = new Series();
Chart.Series.Add(_placeHolder);
_placeHolder.ChartArea = ChartArea.Name;
}
else
{
_placeHolder.Points.Clear();
}
for (int i = 0; i <= value; i++)
{
DataPoint dataPoint = new DataPoint();
dataPoint.IsEmpty = true;
_placeHolder.Points.Add(dataPoint);
}
}
}
public double? MinValue { get; set; }
public double? MaxValue { get; set; }
public MinMaxSummary(Chart chart, ChartArea chartArea = null)
{
Chart = chart;
if (null == chartArea)
ChartArea = chart.ChartAreas[0];
else
ChartArea = chartArea;
if (chart.Series.Count > 0)
X = chart.Series[0].Points.Count;
}
private List _annotations = new List();
public void Hide()
{
foreach (Annotation annotation in _annotations)
{
Chart.Annotations.Remove(annotation);
annotation.Dispose();
}
_annotations.Clear();
}
private static readonly Color MIN_COLOR = Color.FromArgb(0, 192, 192);
private static readonly Color MAX_COLOR = Color.FromArgb(158, 0, 192);
private static readonly Color NEUTRAL_COLOR = Color.FromArgb(79, 96, 192);
private static readonly AnnotationSmartLabelStyle SMART_LABEL_STYLE = new AnnotationSmartLabelStyle();
static MinMaxSummary()
{
SMART_LABEL_STYLE.IsMarkerOverlappingAllowed = true;
// AllowOutsidePlotArea does not do what I would expect. The chart never displays something
// off the screen. If something would have been off the screen, we have two options, move it
// slightly, so it is entirely on the screen or don't show it. That's what this property
// chooses between.
//SMART_LABEL_STYLE.AllowOutsidePlotArea = LabelOutsidePlotAreaStyle.Yes;
// MaxMovingDistance doesn't do what I'd hoped. It only chooses between trying to move
// something so it's all visible, or not displaying it at all.
//SMART_LABEL_STYLE.MaxMovingDistance = 0;
// Ideally i'd be able to show something that is partially off the screen. That would make
// the code easier because I wouldn't have to check for that case myself. I'd like it to just
// be clipped. I can deal with that. The bigger issue seems to be roundoff error. Sometimes
// things right at the edge of the screen seem to appear and disappear, or move, at seemingly
// random times.
}
private void CommonInit(Annotation annotation)
{
annotation.AnchorX = _x + 1;
annotation.IsSizeAlwaysRelative = false;
annotation.AxisX = ChartArea.AxisX;
annotation.AxisY = ChartArea.AxisY;
//annotation.ClipToChartArea = ChartArea.Name;
annotation.SmartLabelStyle = SMART_LABEL_STYLE;
//annotation.SmartLabelStyle = new AnnotationSmartLabelStyle();
//annotation.SmartLabelStyle.IsMarkerOverlappingAllowed = true;
_annotations.Add(annotation);
Chart.Annotations.Add(annotation);
}
private void DrawMax()
{
Axis axisY = ChartArea.AxisY;
double value = MaxValue.Value;
if ((value >= axisY.Minimum) && (value <= axisY.Maximum))
{
HorizontalLineAnnotation line = new HorizontalLineAnnotation();
line.AnchorY = value;
line.LineColor = MAX_COLOR;
line.LineWidth = 3;
line.Width = 0.7;
line.AnchorAlignment = ContentAlignment.MiddleCenter;
CommonInit(line);
}
if (value >= axisY.Minimum)
{
// It seems that if an annotation is partially off the screen, it isn't
// drawn at all. So we manuall cut off anything that would be out of bounds, so
// the rest will be visible. Oddly enough, setting
// SMART_LABEL_STYLE.AllowOutsidePlotArea does not seem to have any effect.
double top = Math.Min(axisY.Maximum, value);
ArrowAnnotation arrow = new ArrowAnnotation();
arrow.AnchorY = top;
arrow.BackColor = MAX_COLOR;
arrow.LineWidth = 0;
arrow.Height = top - axisY.Minimum;
arrow.Width = 0;
arrow.AnchorAlignment = ContentAlignment.BottomCenter;
CommonInit(arrow);
}
}
private void DrawMin()
{
Axis axisY = ChartArea.AxisY;
double value = MinValue.Value;
if ((value >= axisY.Minimum) && (value <= axisY.Maximum))
{
HorizontalLineAnnotation line = new HorizontalLineAnnotation();
line.AnchorY = value;
line.LineColor = MIN_COLOR;
line.LineWidth = 3;
line.Width = 0.7;
line.AnchorAlignment = ContentAlignment.MiddleCenter;
CommonInit(line);
}
if (value <= axisY.Maximum)
{
double bottom = Math.Max(axisY.Minimum, value);
ArrowAnnotation arrow = new ArrowAnnotation();
// Strange. Most of the time AnchorY works. But it seems to have some issues when
// height is negative. Switching from AnchorY to Y seemed to fix everything.
//arrow.AnchorY = axisY.Maximum;
arrow.Y = axisY.Maximum;
arrow.BackColor = MIN_COLOR;
arrow.LineWidth = 0;
arrow.Height = bottom - axisY.Maximum; // Notice that this is a negative number.
arrow.Width = 0;
arrow.AnchorAlignment = ContentAlignment.TopCenter;
CommonInit(arrow);
}
}
public void Update()
{
Hide();
Axis axisY = ChartArea.AxisY;
if ((null == MinValue) && (null == MaxValue))
{ // Everything is allowed.
ArrowAnnotation arrow = new ArrowAnnotation();
arrow.AnchorY = axisY.Minimum;
arrow.ArrowStyle = ArrowStyle.DoubleArrow;
// Left / Center / Right seems to be ignored. Top means to put the
// arrow above AnchorY, i.e. AnchorY touches the bottom of the arrow.
// Bottom means to put the arrow below AnchorY, i.e. AnchorY touches
// the top of the arrow. (This seems to be reversed when the height
// is a negative number. Although, I seem to have a lot of problems
// when height is a negative number.)
arrow.AnchorAlignment = ContentAlignment.TopCenter;
arrow.BackColor = NEUTRAL_COLOR;
arrow.LineWidth = 0;
arrow.Height = axisY.Maximum - axisY.Minimum;
arrow.Width = 0;
CommonInit(arrow);
}
else if (null == MinValue)
// Max is set and min is not.
DrawMax();
else if (null == MaxValue)
// Min is set and max is not.
DrawMin();
else if (MinValue.Value == MaxValue.Value)
{ // One point
HorizontalLineAnnotation line = new HorizontalLineAnnotation();
line.AnchorY = MinValue.Value;
line.AnchorAlignment = ContentAlignment.MiddleCenter;
line.LineColor = NEUTRAL_COLOR;
line.LineWidth = 3;
line.Width = 0.7;
CommonInit(line);
}
else if (MinValue.Value < MaxValue.Value)
{ // Inside range.
VerticalLineAnnotation middleLine = new VerticalLineAnnotation();
double top = Math.Min(axisY.Maximum, MaxValue.Value);
double bottom = Math.Max(axisY.Minimum, MinValue.Value);
middleLine.AnchorY = top;
middleLine.AnchorAlignment = ContentAlignment.BottomCenter;
middleLine.LineColor = NEUTRAL_COLOR;
middleLine.LineWidth = 10;
middleLine.Height = top - bottom;
CommonInit(middleLine);
HorizontalLineAnnotation minLine = new HorizontalLineAnnotation();
minLine.AnchorY = MinValue.Value;
minLine.LineColor = MIN_COLOR;
minLine.LineWidth = 3;
minLine.Width = 0.7;
minLine.AnchorAlignment = ContentAlignment.MiddleCenter;
CommonInit(minLine);
HorizontalLineAnnotation maxLine = new HorizontalLineAnnotation();
maxLine.AnchorY = MaxValue.Value;
maxLine.LineColor = MAX_COLOR;
maxLine.LineWidth = 3;
maxLine.Width = 0.7;
maxLine.AnchorAlignment = ContentAlignment.MiddleCenter;
CommonInit(maxLine);
}
else
{ // Outside range.
DrawMin();
DrawMax();
}
}
}
}