|
//-------------------------------------------------------------
// <copyright company=’Microsoft Corporation’>
// Copyright © Microsoft Corporation. All Rights Reserved.
// </copyright>
//-------------------------------------------------------------
// @owner=alexgor
//=================================================================
// File: AccessibleObject.cs
//
// Namespace: System.Windows.Forms.DataVisualization.Charting.Utilities
//
// Classes: ChartAccessibleObject
//
// Purpose: Chart control accessible object.
//
// Reviewed:
//
//===================================================================
#if WINFORMS_CONTROL
#region Used namespaces
using System;
using System.Drawing;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Globalization;
#endregion // Used namespaces
namespace System.Windows.Forms.DataVisualization.Charting.Utilities
{
internal class ChartAccessibleObject : Control.ControlAccessibleObject
{
#region Fields
// Reference to the chart control
private Chart _chart = null;
// List of chart accessible objects
private List<AccessibleObject> _chartAccessibleObjectList = null;
// Position of the chart in screen coordinates (optianl can be set to empty)
private Point _chartScreenPosition = Point.Empty;
// Chart scaleView transformation matrix
private PointF _chartScale = new PointF(1f, 1f);
#endregion // Fields
#region Constructors
/// <summary>
/// Object constructor.
/// </summary>
/// <param name="chart">Reference to the chart control.</param>
public ChartAccessibleObject(Chart chart) : base(chart)
{
this._chart = chart;
}
#endregion // Constructors
#region Properties
/// <summary>
/// Position of the chart in screen coordinates (optianl can be set to empty)
/// </summary>
public Point ChartScreenPosition
{
get
{
return this._chartScreenPosition;
}
}
/// <summary>
/// Gets the role for the Chart. This is used by accessibility programs.
/// </summary>
public override AccessibleRole Role
{
get
{
return AccessibleRole.Chart;
}
}
#endregion // Properties
#region Methods
/// <summary>
/// Indicates if chart child accessibility objects should be reset
/// </summary>
public void ResetChildren()
{
this._chartAccessibleObjectList = null;
}
/// <summary>
/// Chart child count.
/// </summary>
/// <returns>Number of chart child eleements.</returns>
public override int GetChildCount()
{
// Fill list of chart accessible child elements
if (this._chartAccessibleObjectList == null)
{
this.FillChartAccessibleObjectList();
}
return _chartAccessibleObjectList.Count;
}
/// <summary>
/// Get chart child element by index.
/// </summary>
/// <param name="index">Index of the chart child element.</param>
/// <returns>Chart element accessibility object.</returns>
public override AccessibleObject GetChild(int index)
{
// Fill list of chart accessible child elements
if (this._chartAccessibleObjectList == null)
{
this.FillChartAccessibleObjectList();
}
// Return accessible object by index
if (index >= 0 && index < this._chartAccessibleObjectList.Count)
{
return this._chartAccessibleObjectList[index];
}
return null;
}
/// <summary>
/// Creates a list of chart accessible child elements.
/// </summary>
/// <returns>List of chart accessible child elements.</returns>
private void FillChartAccessibleObjectList()
{
// Create new list
this._chartAccessibleObjectList = new List<AccessibleObject>();
// Chart reference must set first
if (this._chart != null)
{
// Add all Titles into the list
foreach (Title title in this._chart.Titles)
{
this._chartAccessibleObjectList.Add(new ChartChildAccessibleObject(
this,
this,
title,
ChartElementType.Title,
SR.AccessibilityTitleName(title.Name),
title.Text,
AccessibleRole.StaticText));
}
// Add all Legends into the list
foreach (Legend legend in this._chart.Legends)
{
this._chartAccessibleObjectList.Add(new ChartChildLegendAccessibleObject(this, legend));
}
// Add all Chart Areas into the list
foreach (ChartArea chartArea in this._chart.ChartAreas)
{
this._chartAccessibleObjectList.Add(new ChartChildChartAreaAccessibleObject(this, chartArea));
}
// Add all annotations into the list
foreach (Annotation annotation in this._chart.Annotations)
{
TextAnnotation textAnnotation = annotation as TextAnnotation;
if (textAnnotation != null)
{
this._chartAccessibleObjectList.Add(new ChartChildAccessibleObject(
this,
this,
annotation,
ChartElementType.Annotation,
SR.AccessibilityAnnotationName(annotation.Name),
textAnnotation.Text,
AccessibleRole.StaticText));
}
else
{
this._chartAccessibleObjectList.Add(new ChartChildAccessibleObject(
this,
this,
annotation,
ChartElementType.Annotation,
SR.AccessibilityAnnotationName(annotation.Name),
string.Empty,
AccessibleRole.Graphic));
}
}
}
}
/// <summary>
/// Navigates from specified child into specified direction.
/// </summary>
/// <param name="chartChildElement">Chart child element.</param>
/// <param name="chartElementType">Chart child element type.</param>
/// <param name="direction">Navigation direction.</param>
/// <returns>Accessibility object we just navigated to.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "direction"),
System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "chartElementType"),
System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "chartChildElement")]
public AccessibleObject NavigateFromChild(object chartChildElement, ChartElementType chartElementType, AccessibleNavigation direction)
{
// TODO: Not Implemented. Requires Selection Manager code changes. Remove CodeAnalysis.SuppressMessageAttributes
return null;
}
/// <summary>
/// Selects child chart element.
/// </summary>
/// <param name="chartChildElement">Chart child element.</param>
/// <param name="chartElementType">Chart child element type.</param>
/// <param name="selection">Selection actin.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "selection"),
System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "chartElementType"),
System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "chartChildElement")]
public void SelectChild(object chartChildElement, ChartElementType chartElementType, AccessibleSelection selection)
{
// TODO: Not Implemented. Requires Selection Manager code changes. Remove CodeAnalysis.SuppressMessageAttributes
}
/// <summary>
/// Checks if specified chart child element is selected.
/// </summary>
/// <param name="chartChildElement">Chart child element.</param>
/// <param name="chartElementType">Chart child element type.</param>
/// <returns>True if child is selected.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "chartElementType"),
System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "chartChildElement")]
public bool IsChildSelected(object chartChildElement, ChartElementType chartElementType)
{
// TODO: Not Implemented. Requires Selection Manager code changes. Remove CodeAnalysis.SuppressMessageAttributes
return false;
}
/// <summary>
/// Gets chart child element bounds position in screen coordinates
/// </summary>
/// <param name="chartElement">Chart child element.</param>
/// <param name="chartElementType">Chart child element type.</param>
/// <param name="seriesName">Series name.</param>
/// <param name="pointIndex">Series data point index.</param>
/// <returns>Element boundary in screen coordinates.</returns>
public Rectangle GetChildBounds(object chartElement, ChartElementType chartElementType, string seriesName, int pointIndex)
{
// Make sure we have a valid reference on the chart control
Rectangle result = Rectangle.Empty;
if (this._chart != null &&
this._chart.chartPicture != null &&
this._chart.chartPicture.Common != null &&
this._chart.chartPicture.Common.HotRegionsList != null)
{
// Execute chart hit test to initialize list of chart element positions
if (this._chart.chartPicture.Common.HotRegionsList.List == null ||
this._chart.chartPicture.Common.HotRegionsList.List.Count == 0)
{
this._chart.HitTest(0, 0);
}
// Find specified chart element in the list
foreach (HotRegion hotRegion in this._chart.chartPicture.Common.HotRegionsList.List)
{
if (hotRegion.Type == chartElementType)
{
// Determine if region should be processed
bool processRegion = false;
if (chartElementType == ChartElementType.DataPoint || chartElementType == ChartElementType.DataPointLabel)
{
// In case of data point and data point label their series name and label should match
if (hotRegion.SeriesName == seriesName && hotRegion.PointIndex == pointIndex)
{
processRegion = true;
}
}
else if (hotRegion.SelectedObject == chartElement || hotRegion.SelectedSubObject == chartElement)
{
processRegion = true;
}
if (processRegion)
{
RectangleF bounds = hotRegion.BoundingRectangle;
// Conver chart relative coordinates to chart absolute (pixel) coordinates
if (hotRegion.RelativeCoordinates)
{
RectangleF absolute = RectangleF.Empty;
absolute.X = bounds.X * (this._chart.Width - 1) / 100F;
absolute.Y = bounds.Y * (this._chart.Height - 1) / 100F;
absolute.Width = bounds.Width * (this._chart.Width - 1) / 100F;
absolute.Height = bounds.Height * (this._chart.Height - 1) / 100F;
bounds = absolute;
}
// Check if chart should be scaled
Rectangle rect = Rectangle.Round(bounds);
if (this._chartScale.X != 1f || this._chartScale.Y != 1f)
{
SizeF rectSize = rect.Size;
rect.X = (int)(rect.X * this._chartScale.X);
rect.Y = (int)(rect.Y * this._chartScale.Y);
rectSize.Width *= this._chartScale.X;
rectSize.Height *= this._chartScale.Y;
rect.Size = Size.Round(rectSize);
}
// Convert to screen coordinates
if (!this.ChartScreenPosition.IsEmpty)
{
rect.Offset(this.ChartScreenPosition);
}
else
{
rect = this._chart.RectangleToScreen(rect);
}
// If elementd is not gridlines just return the rectangle
if (chartElementType != ChartElementType.Gridlines)
{
return rect;
}
// For gridlines continue accumulation all gridlines positions
if (result.IsEmpty)
{
result = rect;
}
else
{
result = Rectangle.Union(result, rect);
}
}
}
}
}
return result;
}
#endregion // Methods
}
/// <summary>
/// Chart child element accessible object
/// </summary>
internal class ChartChildAccessibleObject : AccessibleObject
{
#region Fields
// Chart element presented by this accessibility object
internal object chartChildObject = null;
// Chart child object type
internal ChartElementType chartChildObjectType = ChartElementType.Nothing;
// Chart accessibility object
internal ChartAccessibleObject chartAccessibleObject = null;
// Chart accessibility object
internal AccessibleObject chartAccessibleParentObject = null;
// Accessible object role
internal AccessibleRole role = AccessibleRole.StaticText;
// Accessible object value
internal string name = string.Empty;
// Accessible object name
internal string objectValue = string.Empty;
// Series name
protected string seriesName = string.Empty;
// Data point index
internal int dataPointIndex = -1;
#endregion // Fields
#region Constructors
/// <summary>
/// Initializes chart element accessibility object with specified title.
/// </summary>
/// <param name="chartAccessibleObject">Chart accessibility object.</param>
/// <param name="chartAccessibleParentObject">The chart accessible parent object.</param>
/// <param name="chartChildObject">Chart child object.</param>
/// <param name="chartChildObjectType">Chart child object type.</param>
/// <param name="name">Chart child object name.</param>
/// <param name="objectValue">Chart child object value.</param>
/// <param name="role">Chart child object role.</param>
public ChartChildAccessibleObject(
ChartAccessibleObject chartAccessibleObject,
AccessibleObject chartAccessibleParentObject,
object chartChildObject,
ChartElementType chartChildObjectType,
string name,
string objectValue,
AccessibleRole role)
{
this.chartAccessibleObject = chartAccessibleObject;
this.chartAccessibleParentObject = chartAccessibleParentObject;
this.chartChildObject = chartChildObject;
this.chartChildObjectType = chartChildObjectType;
this.name = name;
this.role = role;
this.objectValue = objectValue;
}
/// <summary>
/// Initializes chart element accessibility object with specified title.
/// </summary>
/// <param name="chartAccessibleObject">Chart accessibility object.</param>
/// <param name="chartAccessibleParentObject">The chart accessible parent object.</param>
/// <param name="chartChildObject">Chart child object.</param>
/// <param name="chartChildObjectType">Chart child object type.</param>
/// <param name="name">Chart child object name.</param>
/// <param name="objectValue">Chart child object value.</param>
/// <param name="role">Chart child object role.</param>
/// <param name="seriesName">Chart series name.</param>
/// <param name="pointIndex">Chart data point index.</param>
public ChartChildAccessibleObject(
ChartAccessibleObject chartAccessibleObject,
AccessibleObject chartAccessibleParentObject,
object chartChildObject,
ChartElementType chartChildObjectType,
string name,
string objectValue,
AccessibleRole role,
string seriesName,
int pointIndex)
{
this.chartAccessibleObject = chartAccessibleObject;
this.chartAccessibleParentObject = chartAccessibleParentObject;
this.chartChildObject = chartChildObject;
this.chartChildObjectType = chartChildObjectType;
this.name = name;
this.role = role;
this.objectValue = objectValue;
this.seriesName = seriesName;
this.dataPointIndex = pointIndex;
}
#endregion // Constructors
#region Properties
/// <summary>
/// Gets the Bounds for the accessible object.
/// </summary>
public override Rectangle Bounds
{
get
{
return this.chartAccessibleObject.GetChildBounds(this.chartChildObject, this.chartChildObjectType, this.seriesName, this.dataPointIndex);
}
}
/// <summary>
/// Gets parent accessible object
/// </summary>
public override AccessibleObject Parent
{
get
{
if (this.chartAccessibleParentObject != null)
{
return this.chartAccessibleParentObject;
}
return this.chartAccessibleObject;
}
}
/// <summary>
/// Object value
/// </summary>
public override string Value
{
get
{
return this.objectValue;
}
set
{
this.objectValue = value;
}
}
/// <summary>
/// Gets accessible object role
/// </summary>
public override AccessibleRole Role
{
get
{
return this.role;
}
}
/// <summary>
/// Gets accessible object state.
/// </summary>
public override AccessibleStates State
{
get
{
AccessibleStates state = AccessibleStates.Selectable;
if (this.chartAccessibleObject.IsChildSelected(this.chartChildObject, this.chartChildObjectType))
{
state |= AccessibleStates.Selected;
}
return state;
}
}
/// <summary>
/// Gets the name of the accessibility object.
/// </summary>
public override string Name
{
get
{
return this.name;
}
set
{
this.name = value;
}
}
#endregion // Properties
#region Methods
/// <summary>
/// Navigate through chart child objects.
/// </summary>
/// <param name="direction">Navigation direction.</param>
/// <returns>Accessibility object to navigate to.</returns>
public override AccessibleObject Navigate(AccessibleNavigation direction)
{
return this.chartAccessibleObject.NavigateFromChild(this.chartChildObject, this.chartChildObjectType, direction);
}
/// <summary>
/// Selects chart child element.
/// </summary>
/// <param name="selection">Element to select.</param>
public override void Select(AccessibleSelection selection)
{
this.chartAccessibleObject.SelectChild(this.chartChildObject, this.chartChildObjectType, selection);
}
#endregion // Methods
}
/// <summary>
/// Chart legend element accessible object
/// </summary>
internal class ChartChildLegendAccessibleObject : ChartChildAccessibleObject
{
#region Fields
// List of child accessible objects
private List<ChartChildAccessibleObject> _childList = new List<ChartChildAccessibleObject>();
#endregion // Fields
#region Constructor
/// <summary>
/// Object constructor.
/// </summary>
/// <param name="chartAccessibleObject">Chart accessible object.</param>
/// <param name="legend">Chart legend object.</param>
public ChartChildLegendAccessibleObject(ChartAccessibleObject chartAccessibleObject, Legend legend)
: base(
chartAccessibleObject,
chartAccessibleObject,
legend, ChartElementType.LegendArea,
SR.AccessibilityLegendName(legend.Name),
string.Empty,
AccessibleRole.StaticText)
{
// Add legend title as a child element
if (legend.Title.Length > 0)
{
this._childList.Add(new ChartChildAccessibleObject(
chartAccessibleObject,
this,
legend, ChartElementType.LegendTitle,
SR.AccessibilityLegendTitleName(legend.Name),
legend.Title,
AccessibleRole.StaticText));
}
// NOTE: Legend items are dynamically generated and curently are not part of the list
}
#endregion // Constructor
#region Methods
/// <summary>
/// Gets child accessible object.
/// </summary>
/// <param name="index">Index of the child object to get.</param>
/// <returns>Chart child accessible object.</returns>
public override AccessibleObject GetChild(int index)
{
if (index >= 0 && index < this._childList.Count)
{
return this._childList[index];
}
return null;
}
/// <summary>
/// Get number of chart accessible objects.
/// </summary>
/// <returns>Number of chart accessible objects.</returns>
public override int GetChildCount()
{
return this._childList.Count;
}
#endregion // Methods
}
/// <summary>
/// Chart area element accessible object
/// </summary>
internal class ChartChildChartAreaAccessibleObject : ChartChildAccessibleObject
{
#region Fields
// List of child accessible objects
private List<ChartChildAccessibleObject> _childList = new List<ChartChildAccessibleObject>();
#endregion // Fields
#region Constructor
/// <summary>
/// Object constructor.
/// </summary>
/// <param name="chartAccessibleObject">Chart accessible object.</param>
/// <param name="chartArea">Chart area object.</param>
public ChartChildChartAreaAccessibleObject(ChartAccessibleObject chartAccessibleObject, ChartArea chartArea)
: base(
chartAccessibleObject,
chartAccessibleObject,
chartArea, ChartElementType.PlottingArea,
SR.AccessibilityChartAreaName(chartArea.Name),
string.Empty,
AccessibleRole.Graphic)
{
// Add all series shown in the chart area
List<Series> areaSeries = chartArea.GetSeries();
foreach (Series series in areaSeries)
{
this._childList.Add(new ChartChildSeriesAccessibleObject(chartAccessibleObject, this, series));
}
// Add all axes
this.AddAxisAccessibilityObjects(chartAccessibleObject, chartArea.AxisX);
this.AddAxisAccessibilityObjects(chartAccessibleObject, chartArea.AxisY);
this.AddAxisAccessibilityObjects(chartAccessibleObject, chartArea.AxisX2);
this.AddAxisAccessibilityObjects(chartAccessibleObject, chartArea.AxisY2);
}
#endregion // Constructor
#region Methods
/// <summary>
/// Helper method which adds all accessibility objects for specified axis
/// </summary>
/// <param name="chartAccessibleObject">Chart accessibility object.</param>
/// <param name="axis">Axis to add accessibility for.</param>
private void AddAxisAccessibilityObjects(ChartAccessibleObject chartAccessibleObject, Axis axis)
{
// Y axis plus title
if (axis.enabled)
{
this._childList.Add(new ChartChildAccessibleObject(
chartAccessibleObject,
this,
axis,
ChartElementType.Axis,
axis.Name,
string.Empty,
AccessibleRole.Graphic));
if (axis.Title.Length > 0)
{
this._childList.Add(new ChartChildAccessibleObject(
chartAccessibleObject,
this,
axis,
ChartElementType.AxisTitle,
SR.AccessibilityChartAxisTitleName(axis.Name),
axis.Title,
AccessibleRole.StaticText));
}
if (axis.MajorGrid.Enabled)
{
this._childList.Add(new ChartChildAccessibleObject(
chartAccessibleObject,
this,
axis.MajorGrid,
ChartElementType.Gridlines,
SR.AccessibilityChartAxisMajorGridlinesName(axis.Name),
string.Empty,
AccessibleRole.Graphic));
}
if (axis.MinorGrid.Enabled)
{
this._childList.Add(new ChartChildAccessibleObject(
chartAccessibleObject,
this,
axis.MinorGrid,
ChartElementType.Gridlines,
SR.AccessibilityChartAxisMinorGridlinesName(axis.Name),
string.Empty,
AccessibleRole.Graphic));
}
}
}
/// <summary>
/// Gets child accessible object.
/// </summary>
/// <param name="index">Index of the child object to get.</param>
/// <returns>Chart child accessible object.</returns>
public override AccessibleObject GetChild(int index)
{
if (index >= 0 && index < this._childList.Count)
{
return this._childList[index];
}
return null;
}
/// <summary>
/// Get number of chart accessible objects.
/// </summary>
/// <returns>Number of chart accessible objects.</returns>
public override int GetChildCount()
{
return this._childList.Count;
}
#endregion // Methods
}
/// <summary>
/// Chart series element accessible object
/// </summary>
internal class ChartChildSeriesAccessibleObject : ChartChildAccessibleObject
{
#region Fields
// List of child accessible objects
private List<ChartChildAccessibleObject> _childList = new List<ChartChildAccessibleObject>();
#endregion // Fields
#region Constructor
/// <summary>
/// Object constructor.
/// </summary>
/// <param name="chartAccessibleObject">Chart accessible object.</param>
/// <param name="series">Chart series object.</param>
public ChartChildSeriesAccessibleObject(ChartAccessibleObject chartAccessibleObject, AccessibleObject parent, Series series)
: base(
chartAccessibleObject,
parent,
series, ChartElementType.DataPoint,
SR.AccessibilitySeriesName(series.Name),
string.Empty,
AccessibleRole.Graphic)
{
// Add all series data points
int index = 1;
foreach (DataPoint point in series.Points)
{
this._childList.Add(new ChartChildAccessibleObject(
chartAccessibleObject,
this,
point,
ChartElementType.DataPoint,
SR.AccessibilityDataPointName(index),
string.Empty,
AccessibleRole.Graphic,
series.Name,
index - 1));
if (point.Label.Length > 0 || point.IsValueShownAsLabel)
{
this._childList.Add(new ChartChildAccessibleObject(
chartAccessibleObject,
this,
point,
ChartElementType.DataPointLabel,
SR.AccessibilityDataPointLabelName(index),
!String.IsNullOrEmpty(point._lastLabelText) ? point._lastLabelText : point.Label,
AccessibleRole.Text,
series.Name,
index - 1));
}
++index;
}
}
#endregion // Constructor
#region Methods
/// <summary>
/// Gets child accessible object.
/// </summary>
/// <param name="index">Index of the child object to get.</param>
/// <returns>Chart child accessible object.</returns>
public override AccessibleObject GetChild(int index)
{
if (index >= 0 && index < this._childList.Count)
{
return this._childList[index];
}
return null;
}
/// <summary>
/// Get number of chart accessible objects.
/// </summary>
/// <returns>Number of chart accessible objects.</returns>
public override int GetChildCount()
{
return this._childList.Count;
}
#endregion // Methods
}
}
#endif // WINFORMS_CONTROL
|