Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Core / CSharp / System / Windows / Ink / StrokeCollection.cs / 1 / StrokeCollection.cs
//------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------
using MS.Utility;
using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows.Media;
using System.Windows.Ink;
using System.Windows.Input;
using System.Runtime.Serialization;
using System.IO;
using System.Reflection;
using MS.Internal.Ink.InkSerializedFormat;
using MS.Internal.Ink;
using SR = MS.Internal.PresentationCore.SR;
using SRID = MS.Internal.PresentationCore.SRID;
// Primary root namespace for TabletPC/Ink/Handwriting/Recognition in .NET
namespace System.Windows.Ink
{
///
/// Collection of strokes objects which can be operated on in aggregate.
///
[System.ComponentModel.TypeConverter(typeof(StrokeCollectionConverter))]
public partial class StrokeCollection : Collection, INotifyPropertyChanged, INotifyCollectionChanged
{
///
/// The string used to designate the native persistence format
/// for ink data (e.g. used on the clipboard)
///
public static readonly String InkSerializedFormat = "Ink Serialized Format";
/// Creates an empty stroke collection
public StrokeCollection()
{
}
/// Creates a StrokeCollection based on a collection of existing strokes
public StrokeCollection(IEnumerable strokes)
{
if ( strokes == null )
{
throw new ArgumentNullException("strokes");
}
List items = (List)this.Items;
//unfortunately we have to check for dupes with this ctor
foreach ( Stroke stroke in strokes )
{
if ( items.Contains(stroke) )
{
//clear and throw
items.Clear();
throw new ArgumentException(SR.Get(SRID.StrokeIsDuplicated), "strokes");
}
items.Add(stroke);
}
}
/// Creates a collection from ISF data in the specified stream
/// Stream of ISF data
public StrokeCollection(Stream stream)
{
if ( stream == null )
{
throw new ArgumentNullException("stream");
}
if ( !stream.CanRead )
{
throw new ArgumentException(SR.Get(SRID.Image_StreamRead), "stream");
}
Stream seekableStream = GetSeekableStream(stream);
if (seekableStream == null)
{
throw new ArgumentException(SR.Get(SRID.Invalid_isfData_Length), "stream");
}
//this will init our stroke collection
StrokeCollectionSerializer serializer = new StrokeCollectionSerializer(this);
serializer.DecodeISF(seekableStream);
}
/// Save the collection of strokes, including any custom attributes to a stream
/// The stream to save Ink Serialized Format to
/// Flag if set to true the data will be compressed, which can
/// reduce the output buffer size in exchange for slower Save performance.
public virtual void Save(Stream stream, bool compress)
{
if ( stream == null )
{
throw new ArgumentNullException("stream");
}
if ( !stream.CanWrite )
{
throw new ArgumentException(SR.Get(SRID.Image_StreamWrite), "stream");
}
SaveIsf(stream, compress);
}
/// Save the collection of strokes uncompressed, including any custom attributes to a stream
/// The stream to save Ink Serialized Format to
public void Save(Stream stream)
{
Save(stream, true);
}
///
/// Internal method for getting just the byte[] out
///
internal void SaveIsf(Stream stream, bool compress)
{
StrokeCollectionSerializer serializer = new StrokeCollectionSerializer(this);
serializer.CurrentCompressionMode = compress ? CompressionMode.Compressed : CompressionMode.NoCompression;
serializer.EncodeISF(stream);
}
///
/// Private helper to read from a stream to the end and get a byte[]
///
private Stream GetSeekableStream(Stream stream)
{
Debug.Assert(stream != null);
Debug.Assert(stream.CanRead);
if ( stream.CanSeek )
{
int bytesToRead = (int)( stream.Length - stream.Position );
if ( bytesToRead < 1 )
{
return null; //nothing to read
}
//we can write to this
return stream;
}
else
{
//Not all Stream implementations support Length. Streams derived from
//NetworkStream and CryptoStream are the primary examples, but there are others
//theyll throw NotSupportedException
MemoryStream ms = new MemoryStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int count = bufferSize;
while ( count == bufferSize )
{
count = stream.Read(buffer, 0, 4096);
ms.Write(buffer, 0, count);
}
ms.Position = 0;
return ms;
}
}
///
/// Allows addition of objects to the EPC
///
///
///
public void AddPropertyData(Guid propertyDataId, object propertyData)
{
DrawingAttributes.ValidateStylusTipTransform(propertyDataId, propertyData);
object oldValue = null;
if ( ContainsPropertyData(propertyDataId) )
{
oldValue = GetPropertyData(propertyDataId);
this.ExtendedProperties[propertyDataId] = propertyData;
}
else
{
this.ExtendedProperties.Add(propertyDataId, propertyData);
}
// fire notification
OnPropertyDataChanged(new PropertyDataChangedEventArgs(propertyDataId, propertyData, oldValue));
}
///
/// Allows removal of objects from the EPC
///
///
public void RemovePropertyData(Guid propertyDataId)
{
object propertyData = GetPropertyData(propertyDataId);
this.ExtendedProperties.Remove(propertyDataId);
// fire notification
OnPropertyDataChanged(new PropertyDataChangedEventArgs(propertyDataId, null, propertyData));
}
///
/// Allows retrieval of objects from the EPC
///
///
public object GetPropertyData(Guid propertyDataId)
{
if ( propertyDataId == Guid.Empty )
{
throw new ArgumentException(SR.Get(SRID.InvalidGuid), "propertyDataId");
}
return this.ExtendedProperties[propertyDataId];
}
///
/// Allows retrieval of a Array of guids that are contained in the EPC
///
public Guid[] GetPropertyDataIds()
{
return this.ExtendedProperties.GetGuidArray();
}
///
/// Allows the checking of objects in the EPC
///
///
public bool ContainsPropertyData(Guid propertyDataId)
{
return this.ExtendedProperties.Contains(propertyDataId);
}
///
/// Applies the specified transform matrix on every stroke in the collection.
/// This method composes this transform with the existing
/// transform on the stroke.
/// The transform to compose against each Stroke
/// Boolean if true the transform matrix will be applied to StylusTip
/// The StrokeCollection does not maintain a separate transform
/// from each Stroke object. Calling Transform on the collection will
/// cause each individual Stroke to be modified.
/// If the StrokesChanged event fires, the changed parameter will be a pointer to 'this'
/// collection, so any changes made to the changed event args will affect 'this' collection.
public void Transform(Matrix transformMatrix, bool applyToStylusTip)
{
// Ensure that the transformMatrix is invertible.
if ( false == transformMatrix.HasInverse )
throw new ArgumentException(SR.Get(SRID.MatrixNotInvertible), "transformMatrix");
// if transformMatrix is identity or the StrokeCollection is empty
// then no change will occur anyway
if ( transformMatrix.IsIdentity || Count == 0 )
{
return;
}
// Apply the transform to each strokes
foreach ( Stroke stroke in this )
{
// samgeo - Presharp issue
// Presharp gives a warning when get methods might deref a null. It's complaining
// here that 'stroke'' could be null, but StrokeCollection never allows nulls to be added
// so this is not possible
#pragma warning disable 1634, 1691
#pragma warning suppress 6506
stroke.Transform(transformMatrix, applyToStylusTip);
#pragma warning restore 1634, 1691
}
}
///
/// Performs a deep copy of the StrokeCollection.
///
public virtual StrokeCollection Clone()
{
StrokeCollection clone = new StrokeCollection();
foreach ( Stroke s in this )
{
// samgeo - Presharp issue
// Presharp gives a warning when get methods might deref a null. It's complaining
// here that s could be null, but StrokeCollection never allows nulls to be added
// so this is not possible
#pragma warning disable 1634, 1691
#pragma warning suppress 6506
clone.Add(s.Clone());
#pragma warning restore 1634, 1691
}
//
// clone epc if we have them
//
if ( _extendedProperties != null )
{
clone._extendedProperties = _extendedProperties.Clone();
}
return clone;
}
///
/// called by base class Collection<T> when the list is being cleared;
/// raises a CollectionChanged event to any listeners
///
protected override sealed void ClearItems()
{
if ( this.Count > 0 )
{
StrokeCollection removed = new StrokeCollection();
for ( int x = 0; x < this.Count; x++ )
{
( (List)removed.Items ).Add(this[x]);
}
base.ClearItems();
RaiseStrokesChanged(null /*added*/, removed, -1);
}
}
///
/// called by base class RemoveAt or Remove methods
///
protected override sealed void RemoveItem(int index)
{
Stroke removedStroke = this[index];
base.RemoveItem(index);
StrokeCollection removed = new StrokeCollection();
( (List)removed.Items ).Add(removedStroke);
RaiseStrokesChanged(null /*added*/, removed, index);
}
///
/// called by base class Insert, Add methods
///
protected override sealed void InsertItem(int index, Stroke stroke)
{
if ( stroke == null )
{
throw new ArgumentNullException("stroke");
}
if ( this.IndexOf(stroke) != -1 )
{
throw new ArgumentException(SR.Get(SRID.StrokeIsDuplicated), "stroke");
}
base.InsertItem(index, stroke);
StrokeCollection addedStrokes = new StrokeCollection();
( (List)addedStrokes.Items ).Add(stroke);
RaiseStrokesChanged(addedStrokes, null /*removed*/, index);
}
///
/// called by base class set_Item method
///
protected override sealed void SetItem(int index, Stroke stroke)
{
if ( stroke == null )
{
throw new ArgumentNullException("stroke");
}
if ( IndexOf(stroke) != -1 )
{
throw new ArgumentException(SR.Get(SRID.StrokeIsDuplicated), "stroke");
}
Stroke removedStroke = this[index];
base.SetItem(index, stroke);
StrokeCollection removed = new StrokeCollection();
( (List)removed.Items ).Add(removedStroke);
StrokeCollection added = new StrokeCollection();
( (List)added.Items ).Add(stroke);
RaiseStrokesChanged(added, removed, index);
}
///
/// Gets the index of the stroke, or -1 if it is not found
///
/// stroke
///
public new int IndexOf(Stroke stroke)
{
if (stroke == null)
{
//we never allow null strokes
return -1;
}
for (int i = 0; i < Count; i++)
{
if (object.ReferenceEquals(this[i], stroke))
{
return i;
}
}
return -1;
}
///
/// Remove a set of Stroke objects to the collection
///
/// The strokes to remove from the collection
/// Changes to the collection trigger a StrokesChanged event.
public void Remove(StrokeCollection strokes)
{
if ( strokes == null )
{
throw new ArgumentNullException("strokes");
}
if ( strokes.Count == 0 )
{
// NOTICE-2004/06/08-WAYNEZEN:
// We don't throw if an empty collection is going to be removed. And there is no event either.
// This rule is also applied to invoking Clear() with an empty StrokeCollection.
return;
}
int[] indexes = this.GetStrokeIndexes(strokes);
if ( indexes == null )
{
// At least one stroke doesn't exist in our collection. We throw.
ArgumentException ae = new ArgumentException(SR.Get(SRID.InvalidRemovedStroke), "strokes");
//
// we add a tag here so we can check for this in EraserBehavior.OnPointEraseResultChanged
// to determine if this method is the origin of an ArgumentException we harden against
//
ae.Data.Add("System.Windows.Ink.StrokeCollection", "");
throw ae;
}
for ( int x = indexes.Length - 1; x >= 0; x-- )
{
//bypass this.RemoveAt, which calls changed events
//and call our protected List directly
//remove from the back so the indexes are correct
( (List)this.Items ).RemoveAt(indexes[x]);
}
RaiseStrokesChanged(null /*added*/, strokes, indexes[0]);
}
///
/// Add a set of Stroke objects to the collection
///
/// The strokes to add to the collection
/// The items are added to the collection at the end of the list.
/// If the item already exists in the collection, then the item is not added again.
public void Add(StrokeCollection strokes)
{
if ( strokes == null )
{
throw new ArgumentNullException("strokes");
}
if ( strokes.Count == 0 )
{
// NOTICE-2004/06/08-WAYNEZEN:
// We don't throw if an empty collection is going to be added. And there is no event either.
return;
}
int index = this.Count;
//validate that none of the strokes exist in the collection
for ( int x = 0; x < strokes.Count; x++ )
{
Stroke stroke = strokes[x];
if ( this.IndexOf(stroke) != -1 )
{
throw new ArgumentException(SR.Get(SRID.StrokeIsDuplicated), "strokes");
}
}
//add the strokes
//bypass this.AddRange, which calls changed events
//and call our protected List directly
( (List)this.Items ).AddRange(strokes);
RaiseStrokesChanged(strokes, null /*removed*/, index);
}
///
/// Replace
///
///
///
public void Replace(Stroke strokeToReplace, StrokeCollection strokesToReplaceWith)
{
if ( strokeToReplace == null )
{
throw new ArgumentNullException(SR.Get(SRID.EmptyScToReplace));
}
StrokeCollection strokesToReplace = new StrokeCollection();
strokesToReplace.Add(strokeToReplace);
this.Replace(strokesToReplace, strokesToReplaceWith);
}
///
/// Replace
///
///
///
public void Replace(StrokeCollection strokesToReplace, StrokeCollection strokesToReplaceWith)
{
if ( strokesToReplace == null )
{
throw new ArgumentNullException(SR.Get(SRID.EmptyScToReplace));
}
if ( strokesToReplaceWith == null )
{
throw new ArgumentNullException(SR.Get(SRID.EmptyScToReplaceWith));
}
int replaceCount = strokesToReplace.Count;
if ( replaceCount == 0 )
{
ArgumentException ae = new ArgumentException(SR.Get(SRID.EmptyScToReplace), "strokesToReplace");
//
// we add a tag here so we can check for this in EraserBehavior.OnPointEraseResultChanged
// to determine if this method is the origin of an ArgumentException we harden against
//
ae.Data.Add("System.Windows.Ink.StrokeCollection", "");
throw ae;
}
int[] indexes = this.GetStrokeIndexes(strokesToReplace);
if ( indexes == null )
{
// At least one stroke doesn't exist in our collection. We throw.
ArgumentException ae = new ArgumentException(SR.Get(SRID.InvalidRemovedStroke), "strokes");
//
// we add a tag here so we can check for this in EraserBehavior.OnPointEraseResultChanged
// to determine if this method is the origin of an ArgumentException we harden against
//
ae.Data.Add("System.Windows.Ink.StrokeCollection", "");
throw ae;
}
//validate that none of the relplaceWith strokes exist in the collection
for ( int x = 0; x < strokesToReplaceWith.Count; x++ )
{
Stroke stroke = strokesToReplaceWith[x];
if ( this.IndexOf(stroke) != -1 )
{
throw new ArgumentException(SR.Get(SRID.StrokeIsDuplicated), "strokesToReplaceWith");
}
}
//bypass this.RemoveAt / InsertRange, which calls changed events
//and call our protected List directly
for ( int x = indexes.Length - 1; x >= 0; x-- )
{
//bypass this.RemoveAt, which calls changed events
//and call our protected List directly
//remove from the back so the indexes are correct
( (List)this.Items ).RemoveAt(indexes[x]);
}
if ( strokesToReplaceWith.Count > 0 )
{
//insert at the
( (List)this.Items ).InsertRange(indexes[0], strokesToReplaceWith);
}
RaiseStrokesChanged(strokesToReplaceWith, strokesToReplace, indexes[0]);
}
///
/// called by StrokeCollectionSerializer during Load, bypasses Change notification
///
internal void AddWithoutEvent(Stroke stroke)
{
Debug.Assert(stroke != null && IndexOf(stroke) == -1);
( (List)this.Items ).Add(stroke);
}
/// Collection of extended properties on this StrokeCollection
internal ExtendedPropertyCollection ExtendedProperties
{
get
{
//
// internal getter is used by the serialization code
//
if ( _extendedProperties == null )
{
_extendedProperties = new ExtendedPropertyCollection();
}
return _extendedProperties;
}
private set
{
//
// private setter used by copy
//
if ( value != null )
{
_extendedProperties = value;
}
}
}
///
/// Event that notifies listeners whenever a change occurs in the set
/// of stroke objects contained in the collection.
///
/// StrokeCollectionChangedEventHandler
public event StrokeCollectionChangedEventHandler StrokesChanged;
///
/// Event that notifies internal listeners whenever a change occurs in the set
/// of stroke objects contained in the collection.
///
/// StrokeCollectionChangedEventHandler
internal event StrokeCollectionChangedEventHandler StrokesChangedInternal;
///
/// Event that notifies listeners whenever a change occurs in the propertyData
///
/// PropertyDataChangedEventHandler
public event PropertyDataChangedEventHandler PropertyDataChanged;
///
/// INotifyPropertyChanged.PropertyChanged event, explicitly implemented
///
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add { _propertyChanged += value; }
remove { _propertyChanged -= value; }
}
///
/// INotifyCollectionChanged.CollectionChanged event, explicitly implemented
///
event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
{
add { _collectionChanged += value; }
remove { _collectionChanged -= value; }
}
/// Method called on derived classes whenever a drawing attributes
/// change has occurred in the stroke references in the collection
/// The change information for the stroke collection
/// StrokesChanged will not be called when drawing attributes or
/// custom attributes are changed. Changes that trigger StrokesChanged
/// include packets or points changing, modified tranforms, and stroke objects
/// being added or removed from the collection.
/// To ensure that events fire for event listeners, derived classes
/// should call this method.
protected virtual void OnStrokesChanged(StrokeCollectionChangedEventArgs e)
{
if ( null == e )
{
throw new ArgumentNullException("e", SR.Get(SRID.EventArgIsNull));
}
//raise our internal event first. This is used by
//our Renderer and IncrementalHitTester since if they can assume
//they are the first in the delegate chain, they can be optimized
//to not have to handle out of order events caused by 3rd party code
//getting called first
if ( this.StrokesChangedInternal != null)
{
this.StrokesChangedInternal(this, e);
}
if ( this.StrokesChanged != null )
{
this.StrokesChanged(this, e);
}
if ( _collectionChanged != null )
{
//raise CollectionChanged. We support the following
//NotifyCollectionChangedActions
NotifyCollectionChangedEventArgs args = null;
if ( this.Count == 0 )
{
//Reset
Debug.Assert(e.Removed.Count > 0);
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
}
else if ( e.Added.Count == 0 )
{
//Remove
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, e.Removed, e.Index);
}
else if ( e.Removed.Count == 0 )
{
//Add
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, e.Added, e.Index);
}
else
{
//Replace
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, e.Added, e.Removed, e.Index);
}
_collectionChanged(this, args);
}
}
///
/// Method called on derived classes whenever a change occurs in
/// the PropertyData.
///
/// Derived classes should call this method (their base class)
/// to ensure that event listeners are notified
protected virtual void OnPropertyDataChanged(PropertyDataChangedEventArgs e)
{
if ( null == e )
{
throw new ArgumentNullException("e", SR.Get(SRID.EventArgIsNull));
}
if ( this.PropertyDataChanged != null )
{
this.PropertyDataChanged(this, e);
}
}
///
/// Method called when a property change occurs to the StrokeCollection
///
/// The EventArgs specifying the name of the changed property.
/// To follow the guidelines, this method should take a PropertyChangedEventArgs
/// instance, but every other INotifyPropertyChanged implementation follows this pattern.
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if ( _propertyChanged != null )
{
_propertyChanged(this, e);
}
}
///
/// Private helper that starts searching for stroke at index,
/// but will loop around before reporting -1. This is used for
/// Stroke.Remove(StrokeCollection). For example, if we're removing
/// strokes, chances are they are in contiguous order. If so, calling
/// IndexOf to validate each stroke is O(n2). If the strokes are in order
/// this produces closer to O(n), if they are not in order, it is no worse
///
private int OptimisticIndexOf(int startingIndex, Stroke stroke)
{
Debug.Assert(startingIndex >= 0);
for ( int x = startingIndex; x < this.Count; x++ )
{
if ( this[x] == stroke )
{
return x;
}
}
//we didn't find anything on the first pass, now search the beginning
for ( int x = 0; x < startingIndex; x++ )
{
if ( this[x] == stroke )
{
return x;
}
}
return -1;
}
///
/// Private helper that returns an array of indexes where the specified
/// strokes exist in this stroke collection. Returns null if at least one is not found.
///
/// The indexes are sorted from smallest to largest
///
///
private int[] GetStrokeIndexes(StrokeCollection strokes)
{
//to keep from walking the StrokeCollection twice for each stroke, we will maintain an index of
//strokes to remove as we go
int[] indexes = new int[strokes.Count];
for ( int x = 0; x < indexes.Length; x++ )
{
indexes[x] = Int32.MaxValue;
}
int currentIndex = 0;
int highestIndex = -1;
int usedIndexCount = 0;
for ( int x = 0; x < strokes.Count; x++ )
{
currentIndex = this.OptimisticIndexOf(currentIndex, strokes[x]);
if ( currentIndex == -1 )
{
//stroke doe3sn't exist, bail out.
return null;
}
//
// optimize for the most common case... replace is passes strokes
// in contiguous order. Only do the sort if we need to
//
if ( currentIndex > highestIndex )
{
//write current to the next available slot
indexes[usedIndexCount++] = currentIndex;
highestIndex = currentIndex;
continue;
}
//keep in sorted order (smallest to largest) with a simple insertion sort
for ( int y = 0; y < indexes.Length; y++ )
{
if ( currentIndex < indexes[y] )
{
if ( indexes[y] != Int32.MaxValue )
{
//shift from the end
for ( int i = indexes.Length - 1; i > y; i-- )
{
indexes[i] = indexes[i - 1];
}
}
indexes[y] = currentIndex;
usedIndexCount++;
if ( currentIndex > highestIndex )
{
highestIndex = currentIndex;
}
break;
}
}
}
return indexes;
}
// This function will invoke OnStrokesChanged method.
// addedStrokes - the collection which contains the added strokes during the previous op.
// removedStrokes - the collection which contains the removed strokes during the previous op.
private void RaiseStrokesChanged(StrokeCollection addedStrokes, StrokeCollection removedStrokes, int index)
{
StrokeCollectionChangedEventArgs eventArgs =
new StrokeCollectionChangedEventArgs(addedStrokes, removedStrokes, index);
// Invoke OnPropertyChanged
OnPropertyChanged(CountName);
OnPropertyChanged(IndexerName);
// Invoke OnStrokesChanged which will fire the StrokesChanged event AND the CollectionChanged event.
OnStrokesChanged(eventArgs);
}
private void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
// Custom 'user-defined' attributes assigned to this collection
// In v1, these were called Ink.ExtendedProperties
private ExtendedPropertyCollection _extendedProperties = null;
// The private PropertyChanged event
private PropertyChangedEventHandler _propertyChanged;
// private CollectionChanged event raiser
private NotifyCollectionChangedEventHandler _collectionChanged;
///
/// Constants for the PropertyChanged event
///
private const string IndexerName = "Item[]";
private const string CountName = "Count";
//
// Nested types...
//
///
/// ReadOnlyStrokeCollection - for StrokeCollection.StrokesChanged event args...
///
internal class ReadOnlyStrokeCollection : StrokeCollection, ICollection, IList
{
internal ReadOnlyStrokeCollection(StrokeCollection strokeCollection)
{
if ( strokeCollection != null )
{
( (List)this.Items ).AddRange(strokeCollection);
}
}
///
/// Change is not allowed. We would override SetItem, InsertItem etc but
/// they need to be sealed on StrokeCollection to prevent dupes from being added
///
///
protected override void OnStrokesChanged(StrokeCollectionChangedEventArgs e)
{
throw new NotSupportedException(SR.Get(SRID.StrokeCollectionIsReadOnly));
}
///
/// IsReadOnly
///
bool IList.IsReadOnly
{
get { return true; }
}
///
/// IsReadOnly
///
bool ICollection.IsReadOnly
{
get { return true; }
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------
using MS.Utility;
using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows.Media;
using System.Windows.Ink;
using System.Windows.Input;
using System.Runtime.Serialization;
using System.IO;
using System.Reflection;
using MS.Internal.Ink.InkSerializedFormat;
using MS.Internal.Ink;
using SR = MS.Internal.PresentationCore.SR;
using SRID = MS.Internal.PresentationCore.SRID;
// Primary root namespace for TabletPC/Ink/Handwriting/Recognition in .NET
namespace System.Windows.Ink
{
///
/// Collection of strokes objects which can be operated on in aggregate.
///
[System.ComponentModel.TypeConverter(typeof(StrokeCollectionConverter))]
public partial class StrokeCollection : Collection, INotifyPropertyChanged, INotifyCollectionChanged
{
///
/// The string used to designate the native persistence format
/// for ink data (e.g. used on the clipboard)
///
public static readonly String InkSerializedFormat = "Ink Serialized Format";
/// Creates an empty stroke collection
public StrokeCollection()
{
}
/// Creates a StrokeCollection based on a collection of existing strokes
public StrokeCollection(IEnumerable strokes)
{
if ( strokes == null )
{
throw new ArgumentNullException("strokes");
}
List items = (List)this.Items;
//unfortunately we have to check for dupes with this ctor
foreach ( Stroke stroke in strokes )
{
if ( items.Contains(stroke) )
{
//clear and throw
items.Clear();
throw new ArgumentException(SR.Get(SRID.StrokeIsDuplicated), "strokes");
}
items.Add(stroke);
}
}
/// Creates a collection from ISF data in the specified stream
/// Stream of ISF data
public StrokeCollection(Stream stream)
{
if ( stream == null )
{
throw new ArgumentNullException("stream");
}
if ( !stream.CanRead )
{
throw new ArgumentException(SR.Get(SRID.Image_StreamRead), "stream");
}
Stream seekableStream = GetSeekableStream(stream);
if (seekableStream == null)
{
throw new ArgumentException(SR.Get(SRID.Invalid_isfData_Length), "stream");
}
//this will init our stroke collection
StrokeCollectionSerializer serializer = new StrokeCollectionSerializer(this);
serializer.DecodeISF(seekableStream);
}
/// Save the collection of strokes, including any custom attributes to a stream
/// The stream to save Ink Serialized Format to
/// Flag if set to true the data will be compressed, which can
/// reduce the output buffer size in exchange for slower Save performance.
public virtual void Save(Stream stream, bool compress)
{
if ( stream == null )
{
throw new ArgumentNullException("stream");
}
if ( !stream.CanWrite )
{
throw new ArgumentException(SR.Get(SRID.Image_StreamWrite), "stream");
}
SaveIsf(stream, compress);
}
/// Save the collection of strokes uncompressed, including any custom attributes to a stream
/// The stream to save Ink Serialized Format to
public void Save(Stream stream)
{
Save(stream, true);
}
///
/// Internal method for getting just the byte[] out
///
internal void SaveIsf(Stream stream, bool compress)
{
StrokeCollectionSerializer serializer = new StrokeCollectionSerializer(this);
serializer.CurrentCompressionMode = compress ? CompressionMode.Compressed : CompressionMode.NoCompression;
serializer.EncodeISF(stream);
}
///
/// Private helper to read from a stream to the end and get a byte[]
///
private Stream GetSeekableStream(Stream stream)
{
Debug.Assert(stream != null);
Debug.Assert(stream.CanRead);
if ( stream.CanSeek )
{
int bytesToRead = (int)( stream.Length - stream.Position );
if ( bytesToRead < 1 )
{
return null; //nothing to read
}
//we can write to this
return stream;
}
else
{
//Not all Stream implementations support Length. Streams derived from
//NetworkStream and CryptoStream are the primary examples, but there are others
//theyll throw NotSupportedException
MemoryStream ms = new MemoryStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int count = bufferSize;
while ( count == bufferSize )
{
count = stream.Read(buffer, 0, 4096);
ms.Write(buffer, 0, count);
}
ms.Position = 0;
return ms;
}
}
///
/// Allows addition of objects to the EPC
///
///
///
public void AddPropertyData(Guid propertyDataId, object propertyData)
{
DrawingAttributes.ValidateStylusTipTransform(propertyDataId, propertyData);
object oldValue = null;
if ( ContainsPropertyData(propertyDataId) )
{
oldValue = GetPropertyData(propertyDataId);
this.ExtendedProperties[propertyDataId] = propertyData;
}
else
{
this.ExtendedProperties.Add(propertyDataId, propertyData);
}
// fire notification
OnPropertyDataChanged(new PropertyDataChangedEventArgs(propertyDataId, propertyData, oldValue));
}
///
/// Allows removal of objects from the EPC
///
///
public void RemovePropertyData(Guid propertyDataId)
{
object propertyData = GetPropertyData(propertyDataId);
this.ExtendedProperties.Remove(propertyDataId);
// fire notification
OnPropertyDataChanged(new PropertyDataChangedEventArgs(propertyDataId, null, propertyData));
}
///
/// Allows retrieval of objects from the EPC
///
///
public object GetPropertyData(Guid propertyDataId)
{
if ( propertyDataId == Guid.Empty )
{
throw new ArgumentException(SR.Get(SRID.InvalidGuid), "propertyDataId");
}
return this.ExtendedProperties[propertyDataId];
}
///
/// Allows retrieval of a Array of guids that are contained in the EPC
///
public Guid[] GetPropertyDataIds()
{
return this.ExtendedProperties.GetGuidArray();
}
///
/// Allows the checking of objects in the EPC
///
///
public bool ContainsPropertyData(Guid propertyDataId)
{
return this.ExtendedProperties.Contains(propertyDataId);
}
///
/// Applies the specified transform matrix on every stroke in the collection.
/// This method composes this transform with the existing
/// transform on the stroke.
/// The transform to compose against each Stroke
/// Boolean if true the transform matrix will be applied to StylusTip
/// The StrokeCollection does not maintain a separate transform
/// from each Stroke object. Calling Transform on the collection will
/// cause each individual Stroke to be modified.
/// If the StrokesChanged event fires, the changed parameter will be a pointer to 'this'
/// collection, so any changes made to the changed event args will affect 'this' collection.
public void Transform(Matrix transformMatrix, bool applyToStylusTip)
{
// Ensure that the transformMatrix is invertible.
if ( false == transformMatrix.HasInverse )
throw new ArgumentException(SR.Get(SRID.MatrixNotInvertible), "transformMatrix");
// if transformMatrix is identity or the StrokeCollection is empty
// then no change will occur anyway
if ( transformMatrix.IsIdentity || Count == 0 )
{
return;
}
// Apply the transform to each strokes
foreach ( Stroke stroke in this )
{
// samgeo - Presharp issue
// Presharp gives a warning when get methods might deref a null. It's complaining
// here that 'stroke'' could be null, but StrokeCollection never allows nulls to be added
// so this is not possible
#pragma warning disable 1634, 1691
#pragma warning suppress 6506
stroke.Transform(transformMatrix, applyToStylusTip);
#pragma warning restore 1634, 1691
}
}
///
/// Performs a deep copy of the StrokeCollection.
///
public virtual StrokeCollection Clone()
{
StrokeCollection clone = new StrokeCollection();
foreach ( Stroke s in this )
{
// samgeo - Presharp issue
// Presharp gives a warning when get methods might deref a null. It's complaining
// here that s could be null, but StrokeCollection never allows nulls to be added
// so this is not possible
#pragma warning disable 1634, 1691
#pragma warning suppress 6506
clone.Add(s.Clone());
#pragma warning restore 1634, 1691
}
//
// clone epc if we have them
//
if ( _extendedProperties != null )
{
clone._extendedProperties = _extendedProperties.Clone();
}
return clone;
}
///
/// called by base class Collection<T> when the list is being cleared;
/// raises a CollectionChanged event to any listeners
///
protected override sealed void ClearItems()
{
if ( this.Count > 0 )
{
StrokeCollection removed = new StrokeCollection();
for ( int x = 0; x < this.Count; x++ )
{
( (List)removed.Items ).Add(this[x]);
}
base.ClearItems();
RaiseStrokesChanged(null /*added*/, removed, -1);
}
}
///
/// called by base class RemoveAt or Remove methods
///
protected override sealed void RemoveItem(int index)
{
Stroke removedStroke = this[index];
base.RemoveItem(index);
StrokeCollection removed = new StrokeCollection();
( (List)removed.Items ).Add(removedStroke);
RaiseStrokesChanged(null /*added*/, removed, index);
}
///
/// called by base class Insert, Add methods
///
protected override sealed void InsertItem(int index, Stroke stroke)
{
if ( stroke == null )
{
throw new ArgumentNullException("stroke");
}
if ( this.IndexOf(stroke) != -1 )
{
throw new ArgumentException(SR.Get(SRID.StrokeIsDuplicated), "stroke");
}
base.InsertItem(index, stroke);
StrokeCollection addedStrokes = new StrokeCollection();
( (List)addedStrokes.Items ).Add(stroke);
RaiseStrokesChanged(addedStrokes, null /*removed*/, index);
}
///
/// called by base class set_Item method
///
protected override sealed void SetItem(int index, Stroke stroke)
{
if ( stroke == null )
{
throw new ArgumentNullException("stroke");
}
if ( IndexOf(stroke) != -1 )
{
throw new ArgumentException(SR.Get(SRID.StrokeIsDuplicated), "stroke");
}
Stroke removedStroke = this[index];
base.SetItem(index, stroke);
StrokeCollection removed = new StrokeCollection();
( (List)removed.Items ).Add(removedStroke);
StrokeCollection added = new StrokeCollection();
( (List)added.Items ).Add(stroke);
RaiseStrokesChanged(added, removed, index);
}
///
/// Gets the index of the stroke, or -1 if it is not found
///
/// stroke
///
public new int IndexOf(Stroke stroke)
{
if (stroke == null)
{
//we never allow null strokes
return -1;
}
for (int i = 0; i < Count; i++)
{
if (object.ReferenceEquals(this[i], stroke))
{
return i;
}
}
return -1;
}
///
/// Remove a set of Stroke objects to the collection
///
/// The strokes to remove from the collection
/// Changes to the collection trigger a StrokesChanged event.
public void Remove(StrokeCollection strokes)
{
if ( strokes == null )
{
throw new ArgumentNullException("strokes");
}
if ( strokes.Count == 0 )
{
// NOTICE-2004/06/08-WAYNEZEN:
// We don't throw if an empty collection is going to be removed. And there is no event either.
// This rule is also applied to invoking Clear() with an empty StrokeCollection.
return;
}
int[] indexes = this.GetStrokeIndexes(strokes);
if ( indexes == null )
{
// At least one stroke doesn't exist in our collection. We throw.
ArgumentException ae = new ArgumentException(SR.Get(SRID.InvalidRemovedStroke), "strokes");
//
// we add a tag here so we can check for this in EraserBehavior.OnPointEraseResultChanged
// to determine if this method is the origin of an ArgumentException we harden against
//
ae.Data.Add("System.Windows.Ink.StrokeCollection", "");
throw ae;
}
for ( int x = indexes.Length - 1; x >= 0; x-- )
{
//bypass this.RemoveAt, which calls changed events
//and call our protected List directly
//remove from the back so the indexes are correct
( (List)this.Items ).RemoveAt(indexes[x]);
}
RaiseStrokesChanged(null /*added*/, strokes, indexes[0]);
}
///
/// Add a set of Stroke objects to the collection
///
/// The strokes to add to the collection
/// The items are added to the collection at the end of the list.
/// If the item already exists in the collection, then the item is not added again.
public void Add(StrokeCollection strokes)
{
if ( strokes == null )
{
throw new ArgumentNullException("strokes");
}
if ( strokes.Count == 0 )
{
// NOTICE-2004/06/08-WAYNEZEN:
// We don't throw if an empty collection is going to be added. And there is no event either.
return;
}
int index = this.Count;
//validate that none of the strokes exist in the collection
for ( int x = 0; x < strokes.Count; x++ )
{
Stroke stroke = strokes[x];
if ( this.IndexOf(stroke) != -1 )
{
throw new ArgumentException(SR.Get(SRID.StrokeIsDuplicated), "strokes");
}
}
//add the strokes
//bypass this.AddRange, which calls changed events
//and call our protected List directly
( (List)this.Items ).AddRange(strokes);
RaiseStrokesChanged(strokes, null /*removed*/, index);
}
///
/// Replace
///
///
///
public void Replace(Stroke strokeToReplace, StrokeCollection strokesToReplaceWith)
{
if ( strokeToReplace == null )
{
throw new ArgumentNullException(SR.Get(SRID.EmptyScToReplace));
}
StrokeCollection strokesToReplace = new StrokeCollection();
strokesToReplace.Add(strokeToReplace);
this.Replace(strokesToReplace, strokesToReplaceWith);
}
///
/// Replace
///
///
///
public void Replace(StrokeCollection strokesToReplace, StrokeCollection strokesToReplaceWith)
{
if ( strokesToReplace == null )
{
throw new ArgumentNullException(SR.Get(SRID.EmptyScToReplace));
}
if ( strokesToReplaceWith == null )
{
throw new ArgumentNullException(SR.Get(SRID.EmptyScToReplaceWith));
}
int replaceCount = strokesToReplace.Count;
if ( replaceCount == 0 )
{
ArgumentException ae = new ArgumentException(SR.Get(SRID.EmptyScToReplace), "strokesToReplace");
//
// we add a tag here so we can check for this in EraserBehavior.OnPointEraseResultChanged
// to determine if this method is the origin of an ArgumentException we harden against
//
ae.Data.Add("System.Windows.Ink.StrokeCollection", "");
throw ae;
}
int[] indexes = this.GetStrokeIndexes(strokesToReplace);
if ( indexes == null )
{
// At least one stroke doesn't exist in our collection. We throw.
ArgumentException ae = new ArgumentException(SR.Get(SRID.InvalidRemovedStroke), "strokes");
//
// we add a tag here so we can check for this in EraserBehavior.OnPointEraseResultChanged
// to determine if this method is the origin of an ArgumentException we harden against
//
ae.Data.Add("System.Windows.Ink.StrokeCollection", "");
throw ae;
}
//validate that none of the relplaceWith strokes exist in the collection
for ( int x = 0; x < strokesToReplaceWith.Count; x++ )
{
Stroke stroke = strokesToReplaceWith[x];
if ( this.IndexOf(stroke) != -1 )
{
throw new ArgumentException(SR.Get(SRID.StrokeIsDuplicated), "strokesToReplaceWith");
}
}
//bypass this.RemoveAt / InsertRange, which calls changed events
//and call our protected List directly
for ( int x = indexes.Length - 1; x >= 0; x-- )
{
//bypass this.RemoveAt, which calls changed events
//and call our protected List directly
//remove from the back so the indexes are correct
( (List)this.Items ).RemoveAt(indexes[x]);
}
if ( strokesToReplaceWith.Count > 0 )
{
//insert at the
( (List)this.Items ).InsertRange(indexes[0], strokesToReplaceWith);
}
RaiseStrokesChanged(strokesToReplaceWith, strokesToReplace, indexes[0]);
}
///
/// called by StrokeCollectionSerializer during Load, bypasses Change notification
///
internal void AddWithoutEvent(Stroke stroke)
{
Debug.Assert(stroke != null && IndexOf(stroke) == -1);
( (List)this.Items ).Add(stroke);
}
/// Collection of extended properties on this StrokeCollection
internal ExtendedPropertyCollection ExtendedProperties
{
get
{
//
// internal getter is used by the serialization code
//
if ( _extendedProperties == null )
{
_extendedProperties = new ExtendedPropertyCollection();
}
return _extendedProperties;
}
private set
{
//
// private setter used by copy
//
if ( value != null )
{
_extendedProperties = value;
}
}
}
///
/// Event that notifies listeners whenever a change occurs in the set
/// of stroke objects contained in the collection.
///
/// StrokeCollectionChangedEventHandler
public event StrokeCollectionChangedEventHandler StrokesChanged;
///
/// Event that notifies internal listeners whenever a change occurs in the set
/// of stroke objects contained in the collection.
///
/// StrokeCollectionChangedEventHandler
internal event StrokeCollectionChangedEventHandler StrokesChangedInternal;
///
/// Event that notifies listeners whenever a change occurs in the propertyData
///
/// PropertyDataChangedEventHandler
public event PropertyDataChangedEventHandler PropertyDataChanged;
///
/// INotifyPropertyChanged.PropertyChanged event, explicitly implemented
///
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add { _propertyChanged += value; }
remove { _propertyChanged -= value; }
}
///
/// INotifyCollectionChanged.CollectionChanged event, explicitly implemented
///
event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
{
add { _collectionChanged += value; }
remove { _collectionChanged -= value; }
}
/// Method called on derived classes whenever a drawing attributes
/// change has occurred in the stroke references in the collection
/// The change information for the stroke collection
/// StrokesChanged will not be called when drawing attributes or
/// custom attributes are changed. Changes that trigger StrokesChanged
/// include packets or points changing, modified tranforms, and stroke objects
/// being added or removed from the collection.
/// To ensure that events fire for event listeners, derived classes
/// should call this method.
protected virtual void OnStrokesChanged(StrokeCollectionChangedEventArgs e)
{
if ( null == e )
{
throw new ArgumentNullException("e", SR.Get(SRID.EventArgIsNull));
}
//raise our internal event first. This is used by
//our Renderer and IncrementalHitTester since if they can assume
//they are the first in the delegate chain, they can be optimized
//to not have to handle out of order events caused by 3rd party code
//getting called first
if ( this.StrokesChangedInternal != null)
{
this.StrokesChangedInternal(this, e);
}
if ( this.StrokesChanged != null )
{
this.StrokesChanged(this, e);
}
if ( _collectionChanged != null )
{
//raise CollectionChanged. We support the following
//NotifyCollectionChangedActions
NotifyCollectionChangedEventArgs args = null;
if ( this.Count == 0 )
{
//Reset
Debug.Assert(e.Removed.Count > 0);
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
}
else if ( e.Added.Count == 0 )
{
//Remove
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, e.Removed, e.Index);
}
else if ( e.Removed.Count == 0 )
{
//Add
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, e.Added, e.Index);
}
else
{
//Replace
args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, e.Added, e.Removed, e.Index);
}
_collectionChanged(this, args);
}
}
///
/// Method called on derived classes whenever a change occurs in
/// the PropertyData.
///
/// Derived classes should call this method (their base class)
/// to ensure that event listeners are notified
protected virtual void OnPropertyDataChanged(PropertyDataChangedEventArgs e)
{
if ( null == e )
{
throw new ArgumentNullException("e", SR.Get(SRID.EventArgIsNull));
}
if ( this.PropertyDataChanged != null )
{
this.PropertyDataChanged(this, e);
}
}
///
/// Method called when a property change occurs to the StrokeCollection
///
/// The EventArgs specifying the name of the changed property.
/// To follow the guidelines, this method should take a PropertyChangedEventArgs
/// instance, but every other INotifyPropertyChanged implementation follows this pattern.
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if ( _propertyChanged != null )
{
_propertyChanged(this, e);
}
}
///
/// Private helper that starts searching for stroke at index,
/// but will loop around before reporting -1. This is used for
/// Stroke.Remove(StrokeCollection). For example, if we're removing
/// strokes, chances are they are in contiguous order. If so, calling
/// IndexOf to validate each stroke is O(n2). If the strokes are in order
/// this produces closer to O(n), if they are not in order, it is no worse
///
private int OptimisticIndexOf(int startingIndex, Stroke stroke)
{
Debug.Assert(startingIndex >= 0);
for ( int x = startingIndex; x < this.Count; x++ )
{
if ( this[x] == stroke )
{
return x;
}
}
//we didn't find anything on the first pass, now search the beginning
for ( int x = 0; x < startingIndex; x++ )
{
if ( this[x] == stroke )
{
return x;
}
}
return -1;
}
///
/// Private helper that returns an array of indexes where the specified
/// strokes exist in this stroke collection. Returns null if at least one is not found.
///
/// The indexes are sorted from smallest to largest
///
///
private int[] GetStrokeIndexes(StrokeCollection strokes)
{
//to keep from walking the StrokeCollection twice for each stroke, we will maintain an index of
//strokes to remove as we go
int[] indexes = new int[strokes.Count];
for ( int x = 0; x < indexes.Length; x++ )
{
indexes[x] = Int32.MaxValue;
}
int currentIndex = 0;
int highestIndex = -1;
int usedIndexCount = 0;
for ( int x = 0; x < strokes.Count; x++ )
{
currentIndex = this.OptimisticIndexOf(currentIndex, strokes[x]);
if ( currentIndex == -1 )
{
//stroke doe3sn't exist, bail out.
return null;
}
//
// optimize for the most common case... replace is passes strokes
// in contiguous order. Only do the sort if we need to
//
if ( currentIndex > highestIndex )
{
//write current to the next available slot
indexes[usedIndexCount++] = currentIndex;
highestIndex = currentIndex;
continue;
}
//keep in sorted order (smallest to largest) with a simple insertion sort
for ( int y = 0; y < indexes.Length; y++ )
{
if ( currentIndex < indexes[y] )
{
if ( indexes[y] != Int32.MaxValue )
{
//shift from the end
for ( int i = indexes.Length - 1; i > y; i-- )
{
indexes[i] = indexes[i - 1];
}
}
indexes[y] = currentIndex;
usedIndexCount++;
if ( currentIndex > highestIndex )
{
highestIndex = currentIndex;
}
break;
}
}
}
return indexes;
}
// This function will invoke OnStrokesChanged method.
// addedStrokes - the collection which contains the added strokes during the previous op.
// removedStrokes - the collection which contains the removed strokes during the previous op.
private void RaiseStrokesChanged(StrokeCollection addedStrokes, StrokeCollection removedStrokes, int index)
{
StrokeCollectionChangedEventArgs eventArgs =
new StrokeCollectionChangedEventArgs(addedStrokes, removedStrokes, index);
// Invoke OnPropertyChanged
OnPropertyChanged(CountName);
OnPropertyChanged(IndexerName);
// Invoke OnStrokesChanged which will fire the StrokesChanged event AND the CollectionChanged event.
OnStrokesChanged(eventArgs);
}
private void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
// Custom 'user-defined' attributes assigned to this collection
// In v1, these were called Ink.ExtendedProperties
private ExtendedPropertyCollection _extendedProperties = null;
// The private PropertyChanged event
private PropertyChangedEventHandler _propertyChanged;
// private CollectionChanged event raiser
private NotifyCollectionChangedEventHandler _collectionChanged;
///
/// Constants for the PropertyChanged event
///
private const string IndexerName = "Item[]";
private const string CountName = "Count";
//
// Nested types...
//
///
/// ReadOnlyStrokeCollection - for StrokeCollection.StrokesChanged event args...
///
internal class ReadOnlyStrokeCollection : StrokeCollection, ICollection, IList
{
internal ReadOnlyStrokeCollection(StrokeCollection strokeCollection)
{
if ( strokeCollection != null )
{
( (List)this.Items ).AddRange(strokeCollection);
}
}
///
/// Change is not allowed. We would override SetItem, InsertItem etc but
/// they need to be sealed on StrokeCollection to prevent dupes from being added
///
///
protected override void OnStrokesChanged(StrokeCollectionChangedEventArgs e)
{
throw new NotSupportedException(SR.Get(SRID.StrokeCollectionIsReadOnly));
}
///
/// IsReadOnly
///
bool IList.IsReadOnly
{
get { return true; }
}
///
/// IsReadOnly
///
bool ICollection.IsReadOnly
{
get { return true; }
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- Pkcs7Signer.cs
- PeerCredential.cs
- Int16AnimationBase.cs
- MouseGesture.cs
- ValidatingPropertiesEventArgs.cs
- _SingleItemRequestCache.cs
- WebBrowserPermission.cs
- FixUp.cs
- CustomErrorCollection.cs
- SrgsGrammar.cs
- ProfessionalColors.cs
- VirtualPathUtility.cs
- ControlBuilder.cs
- EmptyCollection.cs
- OdbcRowUpdatingEvent.cs
- TextReader.cs
- SiteMapNodeCollection.cs
- EventLogWatcher.cs
- PointCollectionConverter.cs
- SqlDataSourceStatusEventArgs.cs
- MenuRendererClassic.cs
- PropertyFilterAttribute.cs
- TdsParserStaticMethods.cs
- SqlUserDefinedAggregateAttribute.cs
- SessionKeyExpiredException.cs
- XmlSchemaAttributeGroup.cs
- DesignerHierarchicalDataSourceView.cs
- QuotedPrintableStream.cs
- TimeSpanConverter.cs
- ListMarkerSourceInfo.cs
- DBCommandBuilder.cs
- FrameworkRichTextComposition.cs
- RestHandlerFactory.cs
- LabelDesigner.cs
- DefinitionUpdate.cs
- NetNamedPipeBindingCollectionElement.cs
- SpellerStatusTable.cs
- DetailsViewCommandEventArgs.cs
- MembershipValidatePasswordEventArgs.cs
- DesignerVerbCollection.cs
- BaseTemplateCodeDomTreeGenerator.cs
- WebDisplayNameAttribute.cs
- ViewCellSlot.cs
- GridViewCancelEditEventArgs.cs
- XmlCharType.cs
- OracleSqlParser.cs
- ValidationError.cs
- GeneratedView.cs
- PageAsyncTask.cs
- DataGridViewAutoSizeModeEventArgs.cs
- InputLangChangeRequestEvent.cs
- DataBindingHandlerAttribute.cs
- CallSite.cs
- GlobalItem.cs
- ScriptResourceAttribute.cs
- OdbcDataReader.cs
- XmlChoiceIdentifierAttribute.cs
- ToolboxItemCollection.cs
- SrgsDocumentParser.cs
- AspNetSynchronizationContext.cs
- Latin1Encoding.cs
- XamlSerializerUtil.cs
- ListViewGroup.cs
- CollectionView.cs
- CompositeCollection.cs
- SerialStream.cs
- ColumnWidthChangingEvent.cs
- PointLightBase.cs
- StyleCollection.cs
- GCHandleCookieTable.cs
- ConnectAlgorithms.cs
- LinkDescriptor.cs
- TcpStreams.cs
- BatchWriter.cs
- ChannelReliableSession.cs
- CodeCompileUnit.cs
- ExpandedProjectionNode.cs
- _SafeNetHandles.cs
- StylusPlugInCollection.cs
- XmlSerializerSection.cs
- DataColumnMappingCollection.cs
- LineGeometry.cs
- SmtpException.cs
- DesignerDataParameter.cs
- UserPreferenceChangedEventArgs.cs
- FileAuthorizationModule.cs
- typedescriptorpermissionattribute.cs
- EventSinkActivityDesigner.cs
- UnsafeMethods.cs
- DataControlFieldCollection.cs
- TimeSpanConverter.cs
- XmlSchemaSequence.cs
- RichTextBoxAutomationPeer.cs
- GetLastErrorDetailsRequest.cs
- ProxySimple.cs
- Token.cs
- HttpDebugHandler.cs
- ConstantSlot.cs
- LongValidator.cs
- Trace.cs