using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using TradeIdeas.TIProData;
using TradeIdeas.TIProData.Configuration;
using TradeIdeas.TIProData.Interfaces;
namespace TradeIdeas.TIProGUI.CBT
{
[ToolboxItem(false)]
public partial class ConfigDemoHistogram : UserControl, IChild
{
private readonly IContainer _container;
public ConfigDemoHistogram(IContainer container, ConfigurationWindowManager configurationWindowManager, PrepairedStrategy currentStrategy)
{
_container = container;
_configurationWindowManager = configurationWindowManager;
_currentStrategy = currentStrategy;
InitializeComponent();
// Try to init now because we have the data we need. For a top list this will be perfect.
// For an alert window we might send out a request that will eventually be answered. This
// structure is simpler for the main program which can but doesn't have to call
// HistogramInit() directly.
HistogramInit(configurationWindowManager, _container.ConnectionMaster);
// Let's just make this an invariant. This will always be set to one of the three known
// values, if this tab sheet is visible.
// Note that this next line is also responsible for requesting the data, just like if the
// user had changed that combo box.
histogramBackgroundFiltersComboBox.SelectedIndex = HISTOGRAM_FILTER_TYPE_MARKET_IN_GENERAL;
//HistogramAddDemoData(); // This is useful sometimes in debugging.
container.MinTextBox.TextChanged += delegate(object sender, EventArgs e) { HistogramUpdateMinFromTextBox(); };
container.MaxTextBox.TextChanged += delegate(object sender, EventArgs e) { HistogramUpdateMaxFromTextBox(); };
UpdateImages();
}
private void UpdateImages()
{
histogramMinPictureBox.Image = _container.ConnectionMaster.ImageCacheManager.GetFilter("Min" + _container.InternalCode); ;
histogramMaxPictureBox.Image = _container.ConnectionMaster.ImageCacheManager.GetFilter("Max" + _container.InternalCode);
}
///
/// Allow us to make different types of requests at any time, not just in the constructor.
///
private readonly ConfigurationWindowManager _configurationWindowManager;
private readonly PrepairedStrategy _currentStrategy;
///
/// Does it make sense to try to display the histogram?
///
/// This is not a guarantee that we will have any meaningful data. Because the data is on the server
/// side, and it can possibly even change while we are running, the client must be able to display
/// a histogram without crashing, if this is wrong.
///
/// If we are not sure, we typically say yes. (But we can err in the other direction, too.)
///
/// Standard internal code for the filter. I.e. "Price" "BS", etc.
/// True if it's reasonable to try to display this.
static public bool Available(string code)
{
if (null == _histogramAvailable)
// When we are not sure, we say everything is available.
return true;
// This is still an approximation as this information can change on the server side at
// any time.
return _histogramAvailable.Contains(code);
// Test cases:
// Price: Exists for both histogram and words.
// StockTwits average volume: Exists for only histogram.
// (There is no particularly good reason this is not in words. It's probably just too
// new and hasn't been created yet. So that could change.)
// Bid Size: Exists for only words.
// Decimal: Exists for neither.
}
///
/// This is a list of filter codes for which we know how to draw a histogram.
/// null means that we don't know. In that case we typically say everything is legal.
///
/// This is volatile, so that initialization can easily happen from any thread. The
/// HashSet object can never change, but this variable can point to a new hashset at
/// any time.
///
/// The rule is that this starts null and can be replaced by a new hashset at any
/// time. This variable can stay null for any amount of time, but it can never go
/// back to null after it has been filled in.
///
/// We use some very week requirements here to make this easy to use as a library
/// element. Ideally we'd require the main program to give us the information we
/// need up front.
///
/// Note that we only have one of these. Having one per SendManager might be more
/// consistant with the rest of the code. If you are connected to two different
/// servers, these might not be identical. In practice, we try to keep Xetra very
/// similar to the US servers, with similar names, but even so, Xetra is usually
/// behind and will be missing a few values.
///
static private volatile HashSet _histogramAvailable;
///
/// The config window manager automatically retries forever. So there is no reason
/// to create more than one of these requests. Use this variable to determine if
/// we've already sent the request. This is initially false and can be set to true
/// exactly once. Lock when reading or writing this variable.
///
static private bool _histogramInitRequestSent;
///
/// This is used to protect .
///
static private object _lockObject = new object();
///
/// Try to initialize the list of what's available.
/// This function can be called from any thread.
///
/// This can be null.
/// This can be null.
static public void HistogramInit(ConfigurationWindowManager configurationWindowManager,
IConnectionMaster connectionMaster)
{
if ((null != configurationWindowManager)
&& configurationWindowManager.Loaded
&& (configurationWindowManager.ConfigurationType == ConfigurationType.TopList))
{ // Always try to use this data if we have it. Assume that it might be more current
// than the data we've already recorded. That's not completely unreasonable because
// the user defined filters can be changed at any time.
HashSet newSet = new HashSet();
// Note the assumption that exactly the same filters that are available in the
// top list are available here. If that assumption changes, we'll have to make
// bigger changes than this. We use a TopListStrategy to encode the request to
// the server.
foreach (Filter filter in configurationWindowManager.FiltersInOrder)
newSet.Add(filter.InternalCode);
_histogramAvailable = newSet;
}
else if ((null != connectionMaster) && (null == _histogramAvailable))
{ // We consider this action more expensive, so we don't use it unless we currently
// have no data at all.
bool createNewRequest = false;
lock (_lockObject)
{
if (!_histogramInitRequestSent)
{
createNewRequest = true;
_histogramInitRequestSent = true;
}
}
if (createNewRequest)
{
configurationWindowManager = new ConfigurationWindowManager();
configurationWindowManager.LoadFromServer(connectionMaster, ConfigurationType.TopList,
delegate(ConfigurationWindowManager configurationWindowManager1)
{
HistogramInit(configurationWindowManager1, null);
});
}
}
}
private HistogramRequestBuilder _pendingHistogramRequest;
private void OnHistogramResponse(HistogramRequest request)
{
if (InvokeRequired)
Invoke((MethodInvoker)delegate { OnHistogramResponse(request); });
else if ((_pendingHistogramRequest == request) && (!IsDisposed))
{
if (null == request.Result)
{ // Connection problem. Try again.
request.SendRequest();
}
else
{
_pendingHistogramRequest = null;
_histogramSelections = request.Result;
_histogramTotalCount = request.TotalCount;
UpdateHistogramData();
}
}
}
///
/// These are all the positions on the histogram that the user can select. Note that
/// these are not the bars in the histogram, but the spaces between the bars. Also,
/// there will be one of these to the left of the leftmost bar, and one to the right
/// of the right most bar. The use can select any one of these using the track bar.
/// The user can also select two additional items on the track bar; all the way on
/// either side is a blank. This list might be empy, but never.
///
private List _histogramSelections = new List();
///
/// The total number of stocks available. This might be more than if you select the lowest
/// value for min and the largest value for max. This also includes stocks where the current
/// filter is null.
///
private int _histogramTotalCount = 0;
private void HistogramAddDemoData()
{
_histogramSelections.Clear();
HistogramStyle desiredStyle = HistogramStyle.Discrete;
HistogramSelection hs;
if (desiredStyle == HistogramStyle.Continuous)
{ // Start with a simple version of a bell curve. This is all continuous
// data, with no values at exactly the selected numbers.
hs.cumulativeLessCount = 0;
hs.cumulativeMoreCount = hs.cumulativeLessCount;
hs.value = 10;
_histogramSelections.Add(hs);
hs.cumulativeMoreCount = hs.cumulativeLessCount;
hs.cumulativeLessCount += 1;
hs.value += 10;
_histogramSelections.Add(hs);
hs.cumulativeMoreCount = hs.cumulativeLessCount;
hs.cumulativeLessCount += 6;
hs.value += 10;
_histogramSelections.Add(hs);
hs.cumulativeMoreCount = hs.cumulativeLessCount;
hs.cumulativeLessCount += 15;
hs.value += 10;
_histogramSelections.Add(hs);
hs.cumulativeMoreCount = hs.cumulativeLessCount;
hs.cumulativeLessCount += 20;
hs.value += 10;
_histogramSelections.Add(hs);
hs.cumulativeMoreCount = hs.cumulativeLessCount;
hs.cumulativeLessCount += 15;
hs.value += 10;
_histogramSelections.Add(hs);
hs.cumulativeMoreCount = hs.cumulativeLessCount;
hs.cumulativeLessCount += 6;
hs.value += 10;
_histogramSelections.Add(hs);
hs.cumulativeMoreCount = hs.cumulativeLessCount;
hs.cumulativeLessCount += 1;
hs.value += 10;
_histogramSelections.Add(hs);
}
else
{ // Discrete.
hs.cumulativeLessCount = 0;
hs.cumulativeMoreCount = hs.cumulativeLessCount;
hs.value = 10;
//_histogramSelections.Add(hs);
hs.cumulativeLessCount = hs.cumulativeMoreCount;
hs.cumulativeMoreCount += 1;
hs.value += 10;
_histogramSelections.Add(hs);
hs.cumulativeLessCount = hs.cumulativeMoreCount;
hs.cumulativeMoreCount += 6;
hs.value += 10;
_histogramSelections.Add(hs);
hs.cumulativeLessCount = hs.cumulativeMoreCount;
hs.cumulativeMoreCount += 15;
hs.value += 10;
_histogramSelections.Add(hs);
hs.cumulativeLessCount = hs.cumulativeMoreCount;
hs.cumulativeMoreCount += 20;
hs.value += 10;
_histogramSelections.Add(hs);
hs.cumulativeLessCount = hs.cumulativeMoreCount;
hs.cumulativeMoreCount += 15;
hs.value += 10;
_histogramSelections.Add(hs);
hs.cumulativeLessCount = hs.cumulativeMoreCount;
hs.cumulativeMoreCount += 6;
hs.value += 10;
_histogramSelections.Add(hs);
hs.cumulativeLessCount = hs.cumulativeMoreCount;
hs.cumulativeMoreCount += 1;
hs.value += 10;
_histogramSelections.Add(hs);
}
// Add a few extras where the filter value is null.
_histogramTotalCount = hs.cumulativeMoreCount + 100;
// Any time the data changes you should call this.
UpdateHistogramData();
}
private void UpdateHistogramData()
{
// Set the extremes of the track bars. This is required for some invarients used
// in several places.
histogramMinTrackBar.Minimum = -1;
histogramMinTrackBar.Maximum = _histogramSelections.Count;
histogramMaxTrackBar.Minimum = -1;
histogramMaxTrackBar.Maximum = _histogramSelections.Count;
// Try to pick the best display style, continuous vs. discrete.
int totalCount = 0;
int atCount = 0;
if (_histogramSelections.Count > 0)
{
totalCount = HistogramGetAbsoluteMaxCount() - HistogramGetAbsoluteMinCount();
foreach (HistogramSelection hs in _histogramSelections)
{
atCount += hs.cumulativeMoreCount - hs.cumulativeLessCount;
}
}
int betweenCount = totalCount - atCount;
// This makes some since if we assume that the data is close to perfectly
// matching one or the other. That is to say there is almost nothing between
// the selectable items, or almost nothing exactly at the selectable items.
if (atCount >= betweenCount)
_histogramStyle = HistogramStyle.Discrete;
else
_histogramStyle = HistogramStyle.Continuous;
// Redraw everything that depends on this data.
// Update the position of the trackbars, but not the text in the edit controls.
_histogramUpdateInProgress = true;
UpdateHistogramMinTrackBar();
UpdateHistogramMaxTrackBar();
_histogramUpdateInProgress = false;
// Sometimes this is automatic. But when the min and max are both blank, typically
// this explicit call is required.
HistogramUpdateTotals();
mainHistogramPictureBox.Invalidate();
}
private void UpdateHistogramMinTrackBar()
{
UpdateHistogramTrackBar(_container.MinValue, histogramMinTrackBar, histogramMinTrackBar.Minimum);
}
private void UpdateHistogramMaxTrackBar()
{
UpdateHistogramTrackBar(_container.MaxValue, histogramMaxTrackBar, histogramMaxTrackBar.Maximum);
}
///
/// Update one of the track bars to match the other data. For example, when we first
/// initialize, or when the user changes one of the values by typing in a text field.
///
/// Note that the track bar has a limited number of positions. The other types of input
/// may select a value which does not perfectly match any position on the track bar.
/// In that case we try to pick the nearest tickmark.
///
/// The value to find.
/// Where to store the value.
/// Where to leave the slider if we can't find the value.
/// This could happen if the input value is null or the list of possible selections
/// is empty.
private void UpdateHistogramTrackBar(double? newValue, TrackBar trackBar, int defaultValue)
{
if ((!newValue.HasValue) || (_histogramSelections.Count == 0))
{
trackBar.Value = defaultValue;
return;
}
double value = newValue.Value;
if (value <= _histogramSelections[0].value)
{
trackBar.Value = 0;
return;
}
for (int right = 1; right < _histogramSelections.Count; right++)
{
int left = right - 1;
double middle = (_histogramSelections[left].value + _histogramSelections[right].value) / 2.0;
if (value < middle)
{
trackBar.Value = left;
return;
}
}
trackBar.Value = _histogramSelections.Count - 1;
}
///
/// 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 clost 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 _histogramUpdateInProgress = false;
private void HistogramTrackBarScroll(TextBox textBox, TrackBar trackBar)
{
if (!_histogramUpdateInProgress)
{
_histogramUpdateInProgress = true;
if ((trackBar.Value < 0) || (trackBar.Value >= _histogramSelections.Count))
textBox.Text = "";
else
// TODO -- Format this according to the normal formatting rules.
textBox.Text = _histogramSelections[trackBar.Value].value.ToString();
_histogramUpdateInProgress = false;
}
HistogramUpdateTotals();
}
private HistogramSelection? HistogramGetSelection(int index)
{
if ((index < 0) || (index >= _histogramSelections.Count))
return null;
return _histogramSelections[index];
}
///
/// What is the highest count visible on the histogram?
/// Precondition: is not empty.
///
/// The value on the far right side of the histogram.
private int HistogramGetAbsoluteMaxCount()
{
return _histogramSelections[_histogramSelections.Count - 1].cumulativeMoreCount;
}
private int HistogramGetAbsoluteMinCount()
{ // It is tempting to try to compute this. But the way it's used, this
// seems list the most correct possible value.
return 0;
}
private int HistogramStyleAdjustedMax()
{
if (_histogramStyle == HistogramStyle.Continuous)
// The numbers on the track bar refer to the spaces between the bars.
// If the min and max track bar are in the same place, they select nothing.
return histogramMaxTrackBar.Value;
else
// The numbers on the track bar refer to the bars. If min and max are
// both set to 0, we want to select only the first bar (i.e. bar #0).
return histogramMaxTrackBar.Value + 1;
}
private void HistogramUpdateTotals()
{
int count;
HistogramSelection? min = HistogramGetSelection(histogramMinTrackBar.Value);
HistogramSelection? max = HistogramGetSelection(histogramMaxTrackBar.Value);
if ((!min.HasValue) && (!max.HasValue))
{ // If both are empty then we include everything, including some stocks with a null value for this filter.
count = _histogramTotalCount;
_barIsIncluded = delegate { return true; };
}
else if (!max.HasValue)
{ // Min is provided but no max. Assume he seleccted the highest value for the max.
count = HistogramGetAbsoluteMaxCount() - min.Value.cumulativeLessCount;
_barIsIncluded = delegate(int index) { return index >= histogramMinTrackBar.Value; };
}
else if (!min.HasValue)
{ // Max is provided, but not min.
count = max.Value.cumulativeMoreCount - HistogramGetAbsoluteMinCount();
_barIsIncluded = delegate(int index) { return index < HistogramStyleAdjustedMax(); };
}
else if (histogramMaxTrackBar.Value >= histogramMinTrackBar.Value)
{ // See what is between the two values.
count = max.Value.cumulativeMoreCount - min.Value.cumulativeLessCount;
_barIsIncluded = delegate(int index)
{
return (index >= histogramMinTrackBar.Value) && (index < HistogramStyleAdjustedMax());
};
}
else
{ // See what is outside of the two values.
count = (HistogramGetAbsoluteMaxCount() - min.Value.cumulativeLessCount)
+ (max.Value.cumulativeMoreCount - HistogramGetAbsoluteMinCount());
_barIsIncluded = delegate(int index)
{
return (index >= histogramMinTrackBar.Value) || (index < HistogramStyleAdjustedMax());
};
}
histogramTotalLabel.Text = "Count: " + count;
mainHistogramPictureBox.Invalidate();
}
private void HistogramUpdateMinFromTextBox()
{
if (!_histogramUpdateInProgress)
{
_histogramUpdateInProgress = true;
UpdateHistogramMinTrackBar();
_histogramUpdateInProgress = false;
}
}
private void HistogramUpdateMaxFromTextBox()
{
if (!_histogramUpdateInProgress)
{
_histogramUpdateInProgress = true;
UpdateHistogramMaxTrackBar();
_histogramUpdateInProgress = false;
}
}
private void histogramMinTrackBar_ValueChanged(object sender, EventArgs e)
{
HistogramTrackBarScroll(_container.MinTextBox, histogramMinTrackBar);
}
private void histogramMaxTrackBar_ValueChanged(object sender, EventArgs e)
{
HistogramTrackBarScroll(_container.MaxTextBox, histogramMaxTrackBar);
}
private static readonly Brush HISTOGRAM_BACKGROUND = new SolidBrush(Color.FromArgb(255, 255, 255));
private static readonly Brush HISTOGRAM_USED_BAR = new SolidBrush(Color.FromArgb(3, 100, 151));
private static readonly Brush HISTOGRAM_UNUSED_BAR = new SolidBrush(Color.FromArgb(205, 205, 205));
private static readonly Brush MIN_ICON_COLOR = new SolidBrush(Color.FromArgb(255, 0, 192, 192));
private static readonly Brush MAX_ICON_COLOR = new SolidBrush(Color.FromArgb(255, 158, 0, 192));
private delegate bool BarIsIncluded(int index);
///
/// This function tells you whether or not a bar in the histogram will be included in the
/// window based on the current filter values. 0 is the bar on the far left. This can
/// be used to draw the bars in a different color to show the user what currently is or
/// is not included.
///
private BarIsIncluded _barIsIncluded = delegate { return false; };
///
/// This says which data we want to highlight on our histogram, the number of symbols
/// between any two consecutive data points or the number of symbols at each data point.
///
/// Up days for example would probably be labeled as "At". We will have a small number
/// of values to choose from, all integers. For example, maybe -5, -4, -3, -2, -1, 0,
/// 1, 2, 3, 4, 5. The user will be able to select any one of those numbers from the
/// track bar. And we want to see one bar on the histogram for each of those values.
///
/// RSI is on the other extreme. We might be able to select values like 0.0, 0.2, 0.4,
/// 06, 0.8, and 1.0. But we don't expect any stocks to exactly match these values.
/// Each bar on the histogram will correspond to an adjacent pair of these. Bar 0 will
/// show how many stocks have an RSI "Between" 0.0 and 0.2. So if the track bar can
/// select 6 values, we only display 5 bars in the histogram.
///
/// Of course, these are the extremes. Price will be similar to RSI, but we actually
/// expect to see some stocks which exactly match the given price. And it could even
/// happen for RSI, although that might be rare. And, up days could also have some
/// values in between the numbers we select. If there are too many actual value, we
/// might combine some into the same bar. But this answers the basic question, if the
/// track bar can display N points, how many bars are on the histogram.
///
private enum HistogramStyle { Continuous, Discrete };
private HistogramStyle _histogramStyle;
private void mainHistogramPictureBox_Paint(object sender, PaintEventArgs e)
{
Rectangle everything = new Rectangle(0, 0, mainHistogramPictureBox.Width, mainHistogramPictureBox.Height);
e.Graphics.FillRectangle(HISTOGRAM_BACKGROUND, everything);
if (_histogramStyle == HistogramStyle.Continuous)
{ // Draw one bar between each consecutive pair of data points.
if (_histogramSelections.Count < 2)
return;
}
else
{ // Draw one bar for each data point.
if (_histogramSelections.Count < 1)
return;
}
float selectorReservedHeight = 6.0f;
if (everything.Height < selectorReservedHeight * 3)
return;
List barHeights = new List();
if (_histogramStyle == HistogramStyle.Continuous)
{
for (int left = 0; left < _histogramSelections.Count - 1; left++)
barHeights.Add(_histogramSelections[left + 1].cumulativeLessCount - _histogramSelections[left].cumulativeLessCount);
}
else
{
foreach (HistogramSelection hs in _histogramSelections)
{ // For simplicity I'm just ignoring what's between the bars. It would be an inexact approximation
// no matter what. I suppose we could add a third style which shows bars for both the exact positions
// and the stuff between these positions. That would only make sense if there was a lot of data
// in both places, otherwise it would probably add more confusion than it solved (even if it was
// more precise and accurate). Our general assumption at this time is that one of the existing
// styles will be a close match for all of our data.
barHeights.Add(hs.cumulativeMoreCount - hs.cumulativeLessCount);
}
}
int maxCount = barHeights.Max();
if (maxCount <= 0)
return;
RectangleF body = new RectangleF();
body.X = everything.X;
body.Y = everything.Y;
body.Height = everything.Height - selectorReservedHeight;
body.Width = everything.Width;
float totalBarWidth = body.Width / (barHeights.Count + 1);
float barPadding = totalBarWidth / 5.0f;
float filledBarWidth = totalBarWidth - barPadding;
// The selector should look like an L. It should touch the side and bottom of
// the bar.
float selectorWidth = Math.Min(2 * selectorReservedHeight, barPadding);
float selectorHeight = selectorReservedHeight * 3;
HistogramSelection? min = HistogramGetSelection(histogramMinTrackBar.Value);
HistogramSelection? max = HistogramGetSelection(histogramMaxTrackBar.Value);
if ((_histogramStyle == HistogramStyle.Continuous) && min.HasValue
&& max.HasValue && (histogramMinTrackBar.Value == histogramMaxTrackBar.Value))
{ // The drawing code has to treat this as a special case. The min and max are so close that
// we expect little or no data between them. If we didn't make this a special case, it would
// look like we were only excluding a very small range, rather than only including a very
// small range.
RectangleF selector = new RectangleF();
selector.Width = selectorWidth / 2;
selector.X = body.X + (histogramMinTrackBar.Value + 0.5f) * totalBarWidth - selector.Width;
selector.Height = selectorHeight;
selector.Y = everything.Y + (everything.Height - selectorHeight);
e.Graphics.FillRectangle(MIN_ICON_COLOR, selector);
selector.X = selector.X + selector.Width;
e.Graphics.FillRectangle(MAX_ICON_COLOR, selector);
}
else
{
if (min.HasValue)
{ // Draw the min bracket.
RectangleF selector = new RectangleF();
// First draw the taller part, just to the left of the bar.
selector.X = body.X + (histogramMinTrackBar.Value + 0.5f) * totalBarWidth + barPadding * 0.5f - selectorWidth / 2;
selector.Width = selectorWidth / 2;
selector.Height = selectorHeight;
selector.Y = everything.Y + (everything.Height - selectorHeight);
e.Graphics.FillRectangle(MIN_ICON_COLOR, selector);
// Then draw the shorter part right under the bar.
selector.X = selector.X + selectorWidth / 2;
selector.Height = selectorReservedHeight;
selector.Y = everything.Y + (everything.Height - selector.Height);
selector.Width = filledBarWidth / 2;
e.Graphics.FillRectangle(MIN_ICON_COLOR, selector);
}
if (max.HasValue)
{ // Draw the max bracket.
RectangleF selector = new RectangleF();
// First draw the shorter part right under the bar.
selector.X = body.X + (histogramMaxTrackBar.Value + 0.5f) * totalBarWidth - totalBarWidth / 2;
if (_histogramStyle == HistogramStyle.Discrete)
selector.X += totalBarWidth;
selector.Width = filledBarWidth / 2;
selector.Height = selectorReservedHeight;
selector.Y = everything.Y + (everything.Height - selector.Height);
e.Graphics.FillRectangle(MAX_ICON_COLOR, selector);
// Then draw the taller part to the right of the bar.
selector.X = selector.X + filledBarWidth / 2;
selector.Height = selectorHeight;
selector.Y = everything.Y + (everything.Height - selectorHeight);
selector.Width = selectorWidth / 2;
e.Graphics.FillRectangle(MAX_ICON_COLOR, selector);
}
}
int index = 0;
foreach (int h in barHeights)
{
RectangleF bar = new RectangleF();
bar.X = body.X + (index + 0.5f) * totalBarWidth + barPadding * 0.5f;
bar.Width = filledBarWidth;
bar.Height = h / (float)maxCount * body.Height;
bar.Y = body.Y + (body.Height - bar.Height);
e.Graphics.FillRectangle(_barIsIncluded(index) ? HISTOGRAM_USED_BAR : HISTOGRAM_UNUSED_BAR, bar);
index++;
}
}
private void HistogramRequestDataNow()
{ // There is no explicit way to cancel an old request. When we get a response, we compare the request to
// _pendingHistogramRequest and ignore it if it is out of date.
_pendingHistogramRequest = new HistogramRequestBuilder(_configurationWindowManager, _container.ConnectionMaster.SendManager);
_pendingHistogramRequest.FilterString = _container.InternalCode;
_pendingHistogramRequest.ResponseFromServer += OnHistogramResponse;
switch (histogramBackgroundFiltersComboBox.SelectedIndex)
{
case HISTOGRAM_FILTER_TYPE_MARKET_IN_GENERAL:
// Leave the defaults.
break;
case HISTOGRAM_FILTER_TYPE_MY_EXCHANGES:
_pendingHistogramRequest.BaseStrategy = new TopListStrategy();
_pendingHistogramRequest.BaseStrategy.Exchanges = new HashSet(_currentStrategy.Exchanges);
break;
case HISTOGRAM_FILTER_TYPE_ALL_MY_FILTERS:
_pendingHistogramRequest.BaseStrategy = new TopListStrategy(_currentStrategy);
_pendingHistogramRequest.BaseStrategy.History = false;
break;
}
_pendingHistogramRequest.SendRequest();
}
private void histogramBackgroundFiltersComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
HistogramRequestDataNow();
}
///
/// This is a value for histogramBackgroundFiltersComboBox.SelectedIndex.
/// This means that we are ignoring all of the users filters filling in the histogram
/// based on a large number of stocks.
///
private const int HISTOGRAM_FILTER_TYPE_MARKET_IN_GENERAL = 0;
///
/// This is a value for histogramBackgroundFiltersComboBox.SelectedIndex.
/// This means to look at all stocks on one of the exchanges selected by the user.
///
private const int HISTOGRAM_FILTER_TYPE_MY_EXCHANGES = 1;
///
/// This is a value for histogramBackgroundFiltersComboBox.SelectedIndex.
/// This means to look at all of the users settings (filters, exchanges, symbol
/// lists) except for the filter we want to build a histogram on.
///
private const int HISTOGRAM_FILTER_TYPE_ALL_MY_FILTERS = 2;
string IChild.Name
{
get { return "Histogram"; }
}
Control IChild.Body
{
get { return this; }
}
bool IChild.CanShowExample
{
get { return false; }
}
void IChild.OnShow()
{
}
}
}