using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.IO; using TradeIdeas.XML; using TradeIdeas.TIProData; using TradeIdeas.TIProData.Configuration; using System.Threading; using TradeIdeas.TIProGUI.CBT; using System.Windows.Interop; using System.Runtime.InteropServices; namespace TIProDevExtension { /// /// Interaction logic for EarningsDate.xaml /// /// public enum RangeOutput { Premarket, MarketHours, PostMarket } public partial class EarningsDate : UserControl { //we need to tag the cells in the grid //We'll "name" the cells from - 27 to + 27 //excluding any weekend cells as well as cells //in the very first row (weekday title cells) //we'll need this to start painting... int _cellTagNumber = -27; private IContainer _container; private TIRanges tiRanges = new TIRanges(); //This list holds the references to all of the tagged cells(rectangles) in the grid private List _refGrid = new List(); [DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr hObject); public EarningsDate() { InitializeComponent(); //set the grid lines by adding rectangles to each grid position. Each rectangle has a border, //thus giving the appearance of a calendar grid. for (int i = 0; i < calendarGrid.RowDefinitions.Count; i++) { for (int j = 0; j < calendarGrid.ColumnDefinitions.Count; j++) { Rectangle rect = new Rectangle(); rect.Stroke = Brushes.Silver; //border if (i != 0 && j != 0 && j != 6) { rect.Tag = _cellTagNumber.ToString(); //using rect.Name = _cellTagNumber.ToString() throws exception _refGrid.Add(rect); _cellTagNumber++; } Grid.SetColumn(rect, j); Grid.SetRow(rect, i); calendarGrid.Children.Add(rect); } } lblMin.Content = "0.00"; lblMax.Content = "0.00"; } private BitmapSource ToWpfImage(System.Drawing.Bitmap img) { if (img == null) return null; lock (img) { IntPtr hBitmap = img.GetHbitmap(); try { //code snippet courtesy of: http://www.codeproject.com/Articles/104929/Bitmap-to-BitmapSource BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap( img.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); return bitmapSource; } finally { DeleteObject(hBitmap); } } } public void setContainer(IContainer container) //this method is called from the WinForm constructor(ConfigWDemoEarningsDate.cs) { _container = container; _container.MinTextBox.TextChanged += new EventHandler(MinTextBox_TextChanged); _container.MaxTextBox.TextChanged += new EventHandler(MaxTextBox_TextChanged); minPictureBox.Source = ToWpfImage(container.ConnectionMaster.ImageCacheManager.GetFilter("MinEarningD")); maxPictureBox.Source = ToWpfImage(container.ConnectionMaster.ImageCacheManager.GetFilter("MaxEarningD")); } void MaxTextBox_TextChanged(object sender, EventArgs e) { updateTrackBar(maxSlider, _container.MaxValue); updateCalendar(); } void MinTextBox_TextChanged(object sender, EventArgs e) { updateTrackBar(minSlider, _container.MinValue); updateCalendar(); } private void minSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { lblMin.Content = String.Format("{0:0.00}", minSlider.Value); //for debugging...hidden on form trackBarScroll(_container.MinTextBox, minSlider); } private void maxSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { lblMax.Content = String.Format("{0:0.00}", maxSlider.Value); //for debugging...hidden on form trackBarScroll(_container.MaxTextBox, maxSlider); } /// /// From Phil: /// This means that we are updating the min or max value. Normally, /// when a user types in the text box, that changes the track bar, and /// when the user changes the track bar, that changes the tex box. /// This keeps us from an infinate loop. What's more, If a user enters /// a number that does not perfectly match the track bar, the track /// bar should update to the closest position, but the text box should /// stay the same. If the user uses a slider, the text box will only /// display one of a small number of possible values. /// private bool _internalTrackBarChange = false; public void trackBarScroll(System.Windows.Forms.TextBox textBox, Slider trackBar) { if (_internalTrackBarChange) { //a change already in progress //changed the trackbar to match the text boxes return; } _internalTrackBarChange = true; textBox.Text = trackBar.Value.ToString(); _internalTrackBarChange = false; } private void updateTrackBar(Slider trackBar,double? value) { if (_internalTrackBarChange) // A change is already in progress. return; _internalTrackBarChange = true; if (null == value) { } else { double rounded = Math.Round(value.Value); if (rounded > trackBar.Maximum) // This is higher than the highest value the track bar can represent. // So set the track bar to its highest value. trackBar.Value = trackBar.Maximum; else if (rounded < trackBar.Minimum) trackBar.Value = trackBar.Minimum; else trackBar.Value = (double)rounded; } _internalTrackBarChange = false; } private void drawRectangle(Rectangle rect, double a, double b, double c, double d, Color color) { LinearGradientBrush myBrush = new LinearGradientBrush(); myBrush.StartPoint = new Point(0.5, 0); myBrush.EndPoint = new Point(0.5, 1); myBrush.GradientStops.Add(new GradientStop(color, a)); myBrush.GradientStops.Add(new GradientStop(color, b)); myBrush.GradientStops.Add(new GradientStop(Colors.White, c)); myBrush.GradientStops.Add(new GradientStop(Colors.White, d)); rect.Fill = myBrush; } private void updateCalendar() { double? min = null; double? max = null; min = _container.MinValue; max = _container.MaxValue; // Do this just once each time the gui changes. tiRanges.Min = min; tiRanges.Max = max; foreach (Rectangle rect in _refGrid) { //In this loop, we go through each of the cells that are tagged, //then determine how/whether that cell should be painted. int dayNumber = Convert.ToInt32(rect.Tag); Color color = checkColor(dayNumber); //Color color = Colors.Lime; //for testing purposes HashSet selections = tiRanges.WhatToShow(dayNumber); if (selections.Contains(RangeOutput.MarketHours) && selections.Contains(RangeOutput.Premarket) && selections.Contains(RangeOutput.PostMarket)) { SolidColorBrush s = new SolidColorBrush(color); rect.Fill = s; } else if (selections.Contains(RangeOutput.MarketHours) && selections.Contains(RangeOutput.Premarket)) { drawRectangle(rect, 0.0, 0.60, 0.62, 1.0, color); } else if (selections.Contains(RangeOutput.MarketHours) && selections.Contains(RangeOutput.PostMarket)) { drawRectangle(rect, 1.0, 0.34, 0.32, 0.0, color); } else if (selections.Contains(RangeOutput.Premarket)) { drawRectangle(rect, 0, 0.32, 0.34, 1.0, color); } else if (selections.Contains(RangeOutput.PostMarket)) { drawRectangle(rect, 1.0, 0.62, 0.60, 0, color); } else rect.Fill = Brushes.Transparent; } } public Color checkColor(int day) { if (_container.MaxValue >= _container.MinValue) { return Color.FromRgb(79, 96, 192); } if (_container.MinValue > _container.MaxValue ) { if (day > _container.MaxValue) { return Color.FromRgb(158, 0, 192); } else if (day < _container.MinValue) { return Color.FromRgb(0, 192, 192); } } if (_container.MinValue != null && _container.MaxValue == null) { return Color.FromRgb(0, 192, 192); } if (_container.MinValue == null && _container.MaxValue != null) { return Color.FromRgb(158, 0, 192); } return Colors.Transparent; } } public class SimpleRange { private const double BIG_VALUE = 1000000; private const double SMALL_VALUE = -BIG_VALUE; public double Min; public double Max; public void NoMax() { Max = BIG_VALUE; } public void NoMin() { Min = SMALL_VALUE; } public bool Included(double value) { System.Diagnostics.Debug.Assert(Min <= Max); return (value >= Min) && (value <= Max); } public SimpleRange() { NoMin(); NoMax(); } } public class TIRanges { private List _simpleRanges = new List(); private bool Included(double value) { // Included() for the whole set in the union of Included() for each item. foreach (SimpleRange simpleRange in _simpleRanges) { if (simpleRange.Included(value)) return true; } return false; } private double? _min; private double? _max; public double? Min { get { return _min; } set { _min = value; Rebuild(); } } public double? Max { get { return _max; } set { _max = value; Rebuild(); } } public TIRanges() { Rebuild(); } private void Rebuild() { _simpleRanges.Clear(); // Start fresh! if ((!_min.HasValue) && (!_max.HasValue)) { // No filters -- Everything is included. _simpleRanges.Add(new SimpleRange()); } else if (!_min.HasValue) { // Only a max. SimpleRange simpleRange = new SimpleRange(); simpleRange.Max = _max.Value; _simpleRanges.Add(simpleRange); } else if (!_max.HasValue) { // Only a min. SimpleRange simpleRange = new SimpleRange(); simpleRange.Min = _min.Value; _simpleRanges.Add(simpleRange); } else if (_max.Value < _min.Value) { // Max < Min. In Phil-world that means two seperate ranges. // The union of what's below the max and what's above the min. SimpleRange lowerRange = new SimpleRange(); lowerRange.Max = _max.Value; _simpleRanges.Add(lowerRange); SimpleRange upperRange = new SimpleRange(); upperRange.Min = _min.Value; _simpleRanges.Add(upperRange); } else { // Max >= Min. So a single point or a simple range. // We treat a single point as a simple range. SimpleRange simpleRange = new SimpleRange(); simpleRange.Min = _min.Value; simpleRange.Max = _max.Value; _simpleRanges.Add(simpleRange); } } public HashSet WhatToShow(int dayNumber) { HashSet result = new HashSet(); if (Included(dayNumber + 0.25)) result.Add(RangeOutput.Premarket); if (Included(dayNumber + 0.5)) result.Add(RangeOutput.MarketHours); if (Included(dayNumber + 0.75)) result.Add(RangeOutput.PostMarket); return result; } } }